• 一个软件架构师,热爱阅读,观察,思考和行动
  • A software architect who enjoys reading, observing, thinking and doing.

AI Agent实现原理与实践(六):Observability & Operations

1. 前言 在上一篇文章中,我们深入剖析了 Permission, Approval & Sandbox 的设计——Agent 如何在拥有强大执行能力的同时,通过多层防御体系保证安全可控。如果说 Agent Loop 是 Agent 的"心跳",Tool System 是"双手",Context Management 是"记忆系统",SubAgent System 是"团队协作能力",Permission & Sandbox 是"免疫系统",那么 Observability & Operations(可观测性与运维)就是 Agent 的"神经系统"——它决定了 Agent 在生产环境中能否被监控、追踪、调试和治理。 当 Agent 真正部署到生产环境——作为 CLI 工具被数千名开发者日常使用、作为后台服务持续运行数周、作为编码助手修改关键代码——可观测性变成了Agent的基本能力。 可观测性不是简单的打日志,报个监控,而是一组更根本的问题: Agent 的一次运行,事后能不能完整回放和审计? Agent 正在做什么,当前处于什么状态,有没有卡住? 子 Agent、后台任务、定时调度——这些异步执行单元能不能被追踪? 出问题后,第一现场在哪里?如何区分是模型问题、工具问题、上下文问题还是系统问题? Token 消耗、API 延迟、工具调用耗时——这些成本指标能不能被量化和管理? 会话能不能被恢复、归档、导出、切换?这些运维动作是不是系统的一等能力? 本文是 Agent 实现原理系列的第六篇,继续参考 Codex CLI、Claude Code、nanobot、pi 四个主流开源项目的实现,从工程角度总结 Agent 可观测性与运维体系的设计原则和关键权衡。 2. 可观测性是 Agent Runtime 的骨架 2.1 从"打点日志"到"多层信号体系" Agent 的运行过程不是一条直线,而是一棵分叉的树——有主会话、有子 Agent、有后台任务、有定时调度、有工具调用链、有压缩边界、有权限决策点。单靠几条零散的 log,在出问题时根本无法还原"当时到底发生了什么"。 更深一层看,可观测性之于 Agent,就像飞行数据记录仪之于飞机——不是为了日常飞行时盯着看,而是为了两件事: 事后可恢复:会话中断了能继续,系统崩溃了能重建状态 事中可诊断:运行时能判断 Agent 是正常推理、卡在工具执行、还是在等待用户审批 因此,一个 production-grade Agent Runtime 的可观测性,不是"有空再补点日志",而是和主执行链同等级的基础设施。它至少需要覆盖五种不同粒度:会话级(transcript、session metadata)、任务级(background task status、task output)、工具级(tool call 轨迹、shell output、permission 事件)、平台级(analytics、tracing、metrics)、运维级(状态快照、健康检查、成本追踪)。 ...

June 7, 2026 · Skyan

AI Agent实现原理与实践(五):Permission, Approval & Sandbox

1. 前言 在上一篇文章中,我们深入剖析了 SubAgent System 的设计——Agent 如何将复杂任务拆解、委托给多个专职子 Agent 并行执行。如果说 Agent Loop 是 Agent 的"心跳",Tool System 是 Agent 的"双手",Context Management 是 Agent 的"记忆系统",SubAgent System 是 Agent 的"团队协作能力",那么 Permission, Approval & Sandbox(权限、审批与沙箱系统)就是 Agent 的"免疫系统"——它决定了 Agent 在拥有强大执行能力的同时,是否会对宿主环境造成不可逆的破坏。 这其实是一个在Agent规模化之后出现的工程难题,在 Demo 阶段,让 Agent “能跑起来"容易,但当 Agent 真正接入开发者的生产环境——能读写文件、执行 shell 命令、访问网络、修改 Git 历史——安全就不再是"锦上添花”,而是"生死攸关"。 Anthropic 曾经内部维护了一个事故日志,专门记录 Agent 行为失控的案例1:Agent 因误解指令而删除了远程 Git 分支;Agent 把工程师的 GitHub 认证 token 上传到了内部计算集群;Agent 试图对生产数据库执行迁移操作。这些事故的共同特征不是"模型意图作恶",而是模型过度热心(overeager)——它以用户没有预料到的方式主动采取了行动。 更隐蔽的威胁来自 prompt injection:攻击者在网页、文件或工具输出中植入指令,劫持 Agent 偏离用户任务、转向攻击者的目标。2025 年 10 月,Anthropic 发表的 Claude Code 沙箱技术博客开门见山地指出:即使是一次成功的 prompt injection,如果发生在 sandbox 之内,攻击者也什么都拿不到2。 ...

June 7, 2026 · Skyan

AI Agent实现原理与实践(四):SubAgent System

1. 前言 在上一篇文章中,我们深入剖析了 Context Management 的设计——Agent 如何在有限的上下文窗口中管理记忆、压缩历史和治理信息。如果说 Agent Loop 是 Agent 的"心跳",Tool System 是 Agent 的"双手",Context Management 是 Agent 的"记忆系统",那么 SubAgent System(子Agent系统)就是 Agent 的"团队协作能力"——它决定了单个 Agent 能否将复杂任务拆解、委托给多个专职子 Agent 并行执行,再将结果汇合为一个整体产出。 这是 Agent 从"单兵作战"升级为"团队协作"的关键一跃。考虑一个真实的编程场景:用户说"帮我重构这个模块,把数据库层从 PostgreSQL 迁移到 MySQL,同时重写所有测试"。在传统单 Agent 模式下,主 Agent 需要先读 Schema、再改 ORM 代码、再迁移数据、再改写测试——每一步的结果都灌进同一个上下文窗口,很快上下文就会膨胀到填满。更糟的是,这些任务天然可并行(改 ORM 和写测试完全可以同时进行),但单 Agent 只能串行。 SubAgent System 解决了这个根本矛盾,在保持主会话可控的前提下,将独立子任务剥离到独立的执行上下文中,并行推进,结果按需回流。这个设计目标看似简单,但要在工程上做好,涉及一套完整的子系统设计:子 Agent 如何创建?父子之间共享什么、隔离什么?子 Agent 之间如何通信?结果如何汇合?worktree 隔离做到什么程度? 最近这一年来,SubAgent System 的研究已经从实验室走向了超大规模的生产实践。2026 年初,Anthropic 研究员 Nicholas Carlini 进行了一项轰动一时的实验1:用 16 个并行 Claude Agent、近 2,000 个 session、两周时间,从零写出一个 10 万行的 C 编译器。 这个编译器能通过 GCC torture test suite 99% 的测试用例,能成功编译 Linux 6.9 内核、QEMU、FFmpeg、SQLite、Postgres 和 Redis。API 费用约 $20,000。有点小贵,但对比一个资深工程师团队两周的人力成本,已经进入了"经济上可行"的区间。与此同时,Anthropic 工程团队也公开了他们在构建**长运行 Agent(Long-running Agent)**时的系统性实践经验2,提出了一套让 Agent 跨越多个上下文窗口持续工作的 Harness 设计。 ...

June 7, 2026 · Skyan

AI Agent实现原理与实践(三):Context Management

1. 前言 在上一篇文章中,我们深入剖析了 Tool System 的设计——Agent 如何定义、注入、执行和回写工具调用。如果说 Agent Loop 是 Agent 的"心跳",Tool System 是 Agent 的"双手",那么 Context Management(上下文管理)就是 Agent 的"记忆系统"——它决定了 Agent 能记住什么、忘记什么、以及如何在有限的上下文窗口中高效组织信息。 1.1 从 Prompt Engineering 到 Context Engineering 过去几年,应用 AI 领域的焦点一直是 Prompt Engineering——如何写出更好的 prompt,让模型产出更好的结果。但随着 Agent 系统从单轮对话演进为多轮、多工具、长生命周期的自主运行,一个更根本的问题浮现了出来:不只是 prompt 写得好不好,而是进入模型上下文窗口的每一段信息,是否都值得占据那有限的注意力预算。 Anthropic 在其 2025 年的工程博客中正式提出了 Context Engineering 这个概念,将其定义为 Prompt Engineering 的自然演进1: Prompt Engineering 解决的是"怎么写 prompt",Context Engineering 解决的是"什么信息该进入上下文窗口,以最大化达成目标行为的概率"。 这个转变的核心原因是:一个在 loop 中运行的 Agent,每轮都在产生越来越多的数据——tool result、思考过程、中间决策、用户反馈——这些数据都有可能对下一轮推理有用,但不一定都值得放进有限的上下文窗口。Context Engineering 的本质,就是从不断膨胀的信息熵中,反复筛选出那组最小的高信号 token 集合。 1.2 上下文窗口没有免费午餐:Context Rot 这个筛选之所以必要,根源在于 LLM 架构层面的一个基本约束。Anthropic 的研究指出,LLM 存在 上下文衰减(context rot) 现象:随着上下文窗口中的 token 数量增加,模型准确回忆其中信息的能力会下降1。 ...

June 6, 2026 · Skyan

AI Agent实现原理与实践(二):Tool System

1. 前言 在上一篇文章中,我们深入剖析了 Agent Loop 的核心原理——Agent 如何在"思考-行动-观察"的循环中持续迭代,直到完成任务。如果说 Agent Loop 是 Agent 系统的"心跳",那么 Tool System(工具系统)就是 Agent 的"双手"——它决定了 Agent 能做什么、怎么做、以及做的过程中如何保证安全和可控。 在2025年以前,大模型应用流行的形态还是Chatbot,这类应用只能说不能做:它可以给你建议,但无法读你的代码、改你的文件、执行你的命令、搜索你的文档。Tool System 把 LLM 从一个"聊天机器"升级为"能干活的行者"。但这也带来了新的工程挑战:工具怎么定义,才能既让模型理解又让运行时高效执行?工具怎么注入,才能既全面又不撑爆上下文窗口?工具怎么执行,才能既灵活又安全?外部工具(MCP)怎么接入,才能和内建工具一样丝滑? 本文是 Agent 实现原理系列的第二篇,继续参考 Codex CLI、Claude Code、nanobot、pi 四个主流开源项目的实现,从工程角度总结 Tool System 的设计原则和关键权衡。 2. Tool System 的本质 2.1 从"函数调用"到"Tool Runtime" 在深入架构之前,先要澄清一个根本问题:工具到底是什么? 传统软件开发是在两个确定性系统之间建立契约——调用 getWeather("NYC") 每次都会以完全相同的方式获取天气。但 Agent 是非确定性的:同一个用户问题,它可能选择调用天气工具,也可能凭通用知识回答,甚至可能产生幻觉或误解工具用法。Anthropic 在其工具设计最佳实践文章中给出了一个精辟的定义1:工具是确定性系统与非确定性智能体之间的契约。 这个定义意味着,为 Agent 编写工具需要从根本上转变思维——不能再像为其他开发者写 API 那样设计工具,而必须面向一个"聪明但偶尔会犯糊涂、上下文有限、需要引导"的调用者来设计。如果你的工具让模型感到困惑、给了它太多无关信息、或者参数命名模糊,那不是模型的问题,是契约设计的问题。带着这个视角来看后面的 Tool System 架构,很多设计取舍的原因就清晰了。 一个最简单的 tool 实现,可以写成这样: def execute_tool(name, args): tool = tools[name] return tool(args) 这种naïve的实现距离一个"能在真实产品中安全运行的 Tool Runtime"还有很大差距。真实场景需要回答的问题包括: ...

June 6, 2026 · Skyan

AI Agent实现原理与实践(一):Agent Loop

导言 自2025年下半年以来,AI大模型的应用热点从chatbot转变到了智能体Agent。以Claude Code,Codex,OpenClaw等为代表的Agent风靡一时,成为业界热点话题。对于开发者而言,一旦习惯使用Agent来开发代码,就不会再回到过去那种、code completion或者chatbot的开发模式了。随之而来的一个疑问就是,Agent和AI chatbot这样的软件有什么本质区别?事实上,主要经过深入学习和研究Agent原理之后,每个人都会发现一个现象:那就是Agent的开发原理和过去所有的软件都不相同,它是一种AI时代、新的、纯新、毫无争议的新软件架构范式。每一个希望深入应用Agent架构的开发者、希望深入了解Agent的学习者、希望深入优化Agent的高级架构师,都必须先了解Agent的实现原理和最佳实践,才能深刻体会到这种崭新的思想,才能在AI时代快速变化的迷雾中,找到一丝微弱的亮光,指引开发者们前行。 本系列文章以著名的四个Agent项目:Claude Code,OpenAI Codex,Pi,Nanobot作为基础案例,分别介绍Agent的主要机制的设计原理和最佳实践。共分为六章:「Agent Loop」,「Tool System」,「Context Management」,「SubAgent System」,「Permission, Approval & Sandbox」以及「Observability & Operations」,这六章可以从机制上剖析Agent的基本架构。但实际Agent应用的内涵远不止这些内容,Agent的工业级落地还会面临更为复杂的情况,这可能是另外一个话题了。 1. 前言 随着2022年底以ChatGPT为代表的LLM(大语言模型)爆发式发展,AI Agent(智能体)迅速从概念走向工程实践。从早期简单的单轮对话工具,到如今能够自主规划、调用工具、持续迭代的编程Agent(如Cursor、Claude Code、Codex CLI),Agent已经成为大模型应用落地的核心形态。正如OpenAI在其官方博客中所说,Agent Loop是连接用户、模型和工具的枢纽。 作为Agent系统的核心机制,Agent Loop(智能体循环)决定了Agent如何思考、如何行动、如何与外部环境交互。这个循环的本质是:Agent接收用户输入,查询模型进行推理,模型要么产生最终回复,要么请求工具调用;Agent执行工具后将结果回灌到prompt中,再次查询模型——如此反复,直到模型停止发出工具调用、产出面向用户的assistant message为止。这个assistant message不仅是对用户的回应,更标志着Agent Loop的一个终止状态:从Agent的视角看,它的工作已经完成,控制权交还用户。 它看似简单——不过是一个"调模型、执行工具、再调模型"的循环——但要在真实产品中稳定、高效、可扩展地运行,却涉及大量精妙的工程设计和架构取舍。比如,随着对话增长,prompt长度会不断增加,而每个模型都有上下文窗口(context window)限制,因此上下文窗口管理是Agent的核心职责之一。此外,Agent执行的工具调用会修改本地环境(如读写文件),所以Agent的"输出"远不止assistant message,还包括它实际产生的代码变更等副作用。 本文是Agent实现原理系列的第一篇,将从工程实现的角度,深入剖析Agent Loop的核心原理。我们将参考Codex CLI、Claude Code、nanobot、pi等主流开源项目的实现,抽象出Agent Loop的通用模型,分析其关键设计点和工程权衡。 2. Agent Loop的本质 2.1 从"会调LLM的程序"到"Agent Runtime" 一个最朴素的Agent实现,可以写成下面这样: while true: response = call_llm(messages, tools) if response has tool_calls: results = execute_tools(response.tool_calls) messages += [response, results] else: return response.content 这段代码确实构成了Agent Loop的核心骨架,但它距离一个"能在真实产品中运行的Agent Runtime"还有很大差距。真实场景需要回答的问题包括: ...

April 25, 2026 · Skyan

策略调度引擎的演进

在搜索、推荐和广告等高复杂度在线系统中,“策略算法的调度执行”是支撑系统智能的基础能力。为了高效运行各种策略算法,几乎所有大型平台最终都走向了相同的抽象:将策略算法封装为算子(Operator),并通过 DAG(有向无环图)驱动其有序并发执行。这种调度引擎的设计已经历二十余年的演进,并在实践中不断向更高层次的智能和复杂性发展。 策略调度引擎通常位于算法密集型在线服务的执行框架之中。下图展示了一个典型推荐系统的策略流程(搜索和广告系统也类似): graph TD A[解析请求参数] --> B[召回算法1] A --> C[召回算法2] A --> D[召回算法3] B --> E[去重] C --> E D --> E E --> F[过滤] G[历史曝光] --> F F --> H[排序] H --> I[重排序] I --> J[返回结果] 上述流程只是推荐系统的一个高层抽象。真实系统中,这样的流程会跨越数十甚至上百个微服务,每个微服务内部都可能执行一个多步骤策略流水线,规模可达几十到几百万行代码。在这种背景下,如何高效开发各阶段策略,并以流程化方式调度执行,成为系统设计的基础问题。 第一代引擎——算子调度(2004~2014) 第一代引擎诞生于桌面互联网兴起到移动互联网爆发前夜的十年间。当时以搜索和广告为代表的复杂在线系统,开始采用面向对象和微服务的设计思想,并把策略算法抽象为算子对象,由调度引擎顺序执行。 这一代的核心特征是: 策略抽象为算子,实现配置化的调度执行 算子化的设计带来了低耦合、高内聚的好处。推荐各阶段复杂算法被封装为独立算子,不同策略可以交给不同的开发者负责,调度引擎只负责顺序调度,从而实现了调度逻辑与策略逻辑的解耦。 但受制于当时的硬件和语言特性,这一代调度主要是单线程串行执行,对多核 CPU 的利用率极低。随着硬件演进和业务规模增长,这种调度方式逐渐暴露出瓶颈。 第一代策略调度引擎一般没有统一的标准,即使在一个系统中,每个微服务内部都有各自不同的调度框架。配置的方式,调度的方式也各有差别。以广告系统为例,一个典型的高级排序服务内部流程如下: graph TD A[解析请求参数] --> B[获取特征] B --> C[广告触发/基础位索引] C --> D[广告排序] D --> E[获取物料] E --> F[返回结果] 类结构如下: classDiagram class Strategy { <<interface>> } class Scheduler class ParserStrategy class SearchStrategy Scheduler ..> Strategy : Use Strategy <|-- ParserStrategy : Extends Strategy <|-- SearchStrategy : Extends Strategy <|-- OtherStrategies : Extends class OtherStrategies 框架通过Strategy设计模式,实现了逻辑上的抽象和隔离。策略算子的调度以顺序执行为主,并行计算很少。 ...

October 2, 2025 · Skyan

AI编程近期的感受

最近由于项目事情比较多,我大量使用AI coding来帮助我开发,一周写了近两万行代码,基本上都是采用编程Agent来开发,总体感受就是:“码农的工作方式,要翻天了”。 回想起我的编程生涯,从1995年的turbo basic开始,到Visual Basic,再到C++、Java、Python等等前后端开发,总体感受还是主要靠自己手搓代码:先厘清目标,做大致的类与函数设计,再分模块敲代码,接着编译、测试、改 bug、调试、再优化,最后上线。工具能提供的帮助很有限:无非是方法与变量的提示,或是对明显的编译错误、风险点给出提醒。绝大多数时候,仍需自己一行行写代码,再交由编译器校验正确性。粗略估算,规划、编码、验证的时间占比约为 1:1:1—— 这就是传统编程的典型节奏。 2023年ChatGPT刚出现时,一开始我还是不以为然,认为只是AI来帮我生成一些代码片段,我还是需要复制粘贴到编辑器中,只是稍微节省了一点点编码的时间。主要的代码框架还是需要我自己来写,AI只是写一些函数片段而已。没想到两年后,2025年的今天,以Cursor、Claude Code为代表的AI编程工具发展如此迅猛,完全颠覆了我三十年编程习惯,让我在短短一个月内,完全接受了这由AI来主导编程和验证的新模式。 这种AI编程的流程可以简单概括为:不用做太细致的设计规划,想清楚需求后直接提给编程Agent,剩下的交给它就行。 Agent会自己设计方案、写代码、开发单元测试,完成后自动运行单测与程序验证。若失败则会根据报错修复bug,直到所有测试用例通过。整个过程中,我们只需要review代码修改、确认执行命令,其他环节全由AI全自动完成。而且AI模型编程水平之高,代码之规范,编程习惯之好,测试覆盖面之高,远超当前大部分的程序员。规划、编码和验证的时间分布,变成了2:0:1,编码因为交给AI自动完成,所以基本可以忽略不计。我们只需要多点时间想清楚需求,留点时间验证结果是否符合需求,其他精力基本不需要投入。 一开始我还是对AI编程能力持怀疑态度,尤其是对AI能否正确开发和修改大型项目,没有太多信心。然而,当我用AI来帮我完全实现了一个一万多行的基础库之后,我被AI编程彻底折服。这个库如果我手搓开发,预计需要5个完整的工作日,然而用AI来写,我只花了1天时间就高质量搞定,并通过了测试验证,工作效率提升了5倍。更有甚者,当我有一个新的想法,从设想到实现并验证通过,同时还夹杂着开会和其他事情的打断,完成只需要1个小时,而我预估如果在日常工作中手搓这个功能,还需要查文档资料,还会被其他事情打断,至少需要8个小时才能完成——从这个意义上来说,工作效率达到了8倍的提升。这就是为什么我完全接受了AI编程,因为它实实在在帮助我提升了5到8倍的开发效率提升,彻底改变了我三十年的编程方式。 在AI大模型爆发两年之后,2025年终于迎来了真正改变行业的时刻。首当其冲就是编程等脑力创作类的领域,程序员不再需要写真正的代码,而是专注于需求的理解,架构的拆解,接口规范和协议的制定,剩下的编码执行的工作,全部交给编程Agent。在这个时代,程序员更重要的素质,将是对行业需求的理解,对系统架构搭建的经验,对上下游系统的了解,对整个架构环境的认知。而具体编码的工作,则交给一群不知疲倦,打字速度奇快,拥有良好编程习惯的AI程序员来执行。而我们只需要保证,他们的产出结果符合预期即可。 这个判断进一步延续了我之前对于智能密集型组织的预测,即在新型的智能密集型组织中,脑力劳动的边际成本将趋于零,多做一个项目基本不需要增加程序员成本,只需要增加架构设计成本。传统意义上写代码的程序员,必然将被编程Agent所代替,且其成本将变成软件订阅费这类的运营支出(OPEX)。这将是对组织形态的一个巨大变化,“订阅一个cursor pro胜过招募五个初级程序员”,这种事正在从想象变成现实。 但这并不意味着程序员就要被历史所淘汰。这个职业必然迎来一次转变:即从代码开发者,升级为“设计师”或者“架构师”。未来的程序员还是需要对技术有深入的了解,加速学习产品和业务知识,能将业务问题转换为技术问题,剩下的交给一群不知疲倦的编程Agent来完成。程序员将更多担任桥梁的作用,理解需求,拆解需求,规划系统架构。这很像从手工业到工业时代的转变:工人不再靠复杂手工打造产品,而是通过机械化流水线生产,更多人力用于发掘市场机会、设计产品、验证想法。 这仅仅是一个开始!AI将会继续改造我们身边的各行各业,所有我们曾经认为“只有人类才能胜任”的脑力工作,都将被重新定义。我们既是这场变革的推动者,也是被其影响的参与者。唯有保持终身学习,才有可能在这场AI大变革中,找到自己的位置。

August 8, 2025 · Skyan

LLM推理优化技术(三)

5. 解码优化 5.1 Speculative Decoding 投机解码(推测解码)1是一种非常成熟的优化技术,并被吸收进各开源大模型推理引擎项目中。它的基本原理非常简单:由于decoding阶段是内存带宽瓶颈限制,LLM输入一个token前向推理一次花费的时间,和批量输入N个token前向推理一次的时间基本一致。那么就有个比较巧妙的办法是,通过某种方法让一次前向推理输出N个token作为草稿(draft),然后批量输入这N个draft token并行前向推理,比较draft token和对应位置的LLM输出token,如果一致则直接跳过推理,如果不支持则从这个位置重新开始下一轮推理。如果猜测的N个draft token准确率足够高,那么整个推理过程将会从每次推理1个token,提升到多个token,推理速度显著提升。 思想很简单,但到具体实现,需要考虑的是如何解决一次推理能猜测N个后续token,并保证一定的准确率。这里就有两种常见的方法: 通过一个小模型来快速预测后续N个token。这个小模型可以是一个参数量更小的LLM,例如Llama 7B作为Llama 70B的猜测模型,也可以是一个更加轻量的n-gram的统计模型,可以根据实际需求挑选。这种方法的好处是,不需要重新训练模型,只需要升级推理过程,即可享受解码加速。缺点是需要两个模型配合,可能会浪费一些内存和算力。 通过修改LLM网络结构,增加多个推测头(speculative head),使得模型一次推理可以同时输出多个token。典型有Medusa2和Multi-token Prediction3。这种方法的好处是比较节省算力和内存资源,缺点也很明显,需要后训练模型,增加网络训练参数。 一个经典的,通过小模型作为draft model的投机解码算法如下图所示: 在实际落地过程中,投机解码中的draft model,也可以是一个n-gram统计模型,这样可以极大减少draft model的开销,而且适应性更强,比较适合输入和输出有较多重复内容的场景,例如文本总结场景。 应用投机解码过程中,还有一个绕不开的话题是投机解码是否对模型效果无损。投机解码影响模型效果主要有三个方面: 浮点数精度影响。由于硬件对于浮点数运算过程中,还是有可能引入精度误差,最终导致结果差异 算法实现影响。不同推理引擎在实现投机解码时,对于sampling阶段的算法,如果无法保证和原模型的分布一致,则会产生结果的差异 随机因素影响。当批量大小不同,GPU数值计算精度将会有细微差别,这个在pytorch等深度学习框架都有类似问题。这个不稳定性累计下来会影响logprobs结果,最终导致输出的差异。 综合上述原因,投机解码还是会或多或少影响LLM的输出效果。需要实际应用时充分完成效果的评测,才能确保该优化是否可使用。 5.2 MoE(Mixture of Experts) LLM在训练时遵循scaling law的规律,即参数量越大,计算量越大,训练token量越大,模型的效果越好。但这种scaling law对推理并不友好。随着参数量的增长,推理的计算量也随之增长,导致推理成本的上升。在深度学习时代,MoE技术就已经被深入研究,可以有效解决训练和推理算力需求不平等问题。 MoE是一种稀疏模型结构。它通过在网络中增加一个门结构来路由多个子网络,在推理过程中动态选择部分子网络参与推理计算,而训练时则是将所有子网络都参与训练参数中。这样既可以增加训练时的模型参数量,也可以有效降低推理时参与计算的参数量。 MoE的典型网络结构,可以参考DeepSeek-V3的结构4,如下图所示: LLM MoE一般将FFN结构替换为多个子FFN网络,在Multi Head Attention结构之后,增加一个门算子,决定路由到哪几个子FFN网络进一步计算。一般子专家网络分为共享专家和路由专家,共享专家是每次推理都会参与计算的子网络,而路由专家则是根据门算子决定选择其中几路专家网络参与计算。 对于数百B量级的MoE大模型的推理部署,是天然适合多GPU并行部署的。例如著名的DeepSeek-V3模型4,每个MoE层有1个共享专家网络,256个子专家网络,每个token推理将会激活8路专家网络和1路共享专家网络。 在部署的时候,Prefilling集群最小部署套餐是4台节点32张H100。每个GPU卡部署8个专家网络,为了避免不同专家计算量的不平衡,实际的Prefilling集群部署的时候,每个GPU卡在8个专家网络之外,还额外部署了一个冗余专家,这样每个token推理激活9个专家网络。通过动态跨节点迁移这个冗余专家网络,来尽量实现计算量的动态平衡。 而V3模型的Decoding集群,最小部署套餐是40个节点320张H100,其中256个GPU卡,每卡部署1个专家网络,其余64个GPU卡,每卡部署冗余专家以及共享专家网络。所有卡间通过直接点到点的IB网络来通信。整个调度需要精巧的设计,来降低通信耗时。 MoE的网络结构天然只会部分激活参数,因此对于推理更加友好。特别是随着DeepSeek的大火,这种网络结构也受到越来越多的关注和重视。 5.3 算子融合 算子融合是一种常见的成熟模型推理优化技术。它将模型网络计算图中的多个算子或者多层,进行编译期融合,来降低中间数据交换,降低计算开销,从而达到提升推理性能的目标。 自从深度学习迅速发展以来,算子融合就已经成为推理优化方向成熟的技术。著名的TVM5开源项目,实现全自动化的编译期算子识别和融合,覆盖经典的深度学习模型。 然而进入大语言模型时代,特别是以decoding-only模式大行其道的今天,专门针对decoding网络的优化更加适合当前业界的需要。著名的FlashAttention6,FlashAttention27,FlashDecoding8,FlashDecoding++9算法,将attension的多步算子计算,融合为一个算子。这样极大减少了读写attention矩阵的开销,提升了LLM推理计算中最重要的算子的性能。 开源项目DeepSpeed-inference10提出了Deep-Fusion融合技术,实现了: QKV矩阵计算和输入层归一化的融合 转置和attention算子的融合 后attention层归一化和中间矩阵计算的融合 偏置叠加和残差网络的融合 xFormers11 提供了各种可以增强Transformer网络结构性能的融合内核。这些包括融合 softmax算子,融合线性层、融合层归一化,以及融合SwiGLU。 TensorRT-LLM 12 是NVidia开发的提供各种高性能融合内核的框架。它集成了一个强大的模式匹配算法,可以检测各种 LLM 中的潜在融合。由于TensorRT-LLM和NVidia GPU紧密结合,使之成为NVidia GPU上推理服务的重要优化工具,深受NVidia GPU开发者的喜爱。 5.3 MQA, GQA, MLA 解码优化的另一个方向,就是在多头注意力模型的attention结构中进行优化。这也需要和训练相配合,而不是纯粹的推理优化。 经典的多头注意力算法(Multiple Head Attention)13,如下图所示: ...

February 20, 2025 · Skyan

LLM推理优化技术(二)

4. 系统优化 4.1 Iteration-level Scheduling 传统上的模型推理批量计算都是将一批请求合并到一起,然后一起发送到GPU批量推理计算,完成后返回结果。但这会引起两个问题: 大语言模型请求到达时间不固定,这样先到的请求得等待后到的请求集齐一个批量,并且等待上一批量计算完成之后,才能一起发送到GPU进行批量推理计算,这对于先到的请求而言,浪费了等待时间 大语言模型每次输入和输出序列的长度不定,一般会采用padding的方法插入一些空白token用于对齐。这种批量计算对于较短的序列并不公平,它虽然已经完成,但仍然等待长序列完成之后才能返回,而且空白token用于padding计算也是浪费,GPU整体利用率也很低。 为此,2022年Orca论文1提出了iteration-level scheduling方法,将每次批量decoding作为一个迭代(iteration),每次批量decoding输出一批token之后,检查有没有完成输出的序列,如果有则从批量token序列中删除,而从排队的请求中挑选一个请求序列插入到空出的序列位置。 通过这种方法,可以有效节省等待时间,只要一个迭代中有完成的请求,就可以加入新请求继续批量decoding,而且还无需特别处理长度不一致导致的padding。这种方法将会极大提升GPU利用率,并在各个主流LLM引擎中得到了广泛应用,例如vLLM和Huggingface TGI的continuous batching,TensorRT-LLM的In-flight Batching,都是这种技术的实际应用。 4.2 Chunked Prefills Iteration-level scheduling可以极大提升GPU利用率,但还未能将GPU利用率压榨到极致。主要有两点问题: 如前所述,推理中的Prefill阶段是计算密集型,Decoding阶段是内存密集型。GPU执行prefill阶段任务时,由于大部分计算都是矩阵和矩阵乘法,因此GPU利用率可以达到最大。但当GPU在执行decoding计算时,由于大部分计算都是向量和矩阵乘法,计算受到内存带宽瓶颈限制,整体利用率很难达到最大化。 由于prefill和decoding阶段分别批量计算,存在两个阶段调度互相抢占问题。如果优先decoding,则会影响首token延迟(TTFT)。如果优先prefill,则会影响生成token间耗时(ITL)。这些调度方法都对整体的延迟长尾不友好。 Chunked prefill技术2通过将prefill拆分为多段,和decoding的序列拼在一起批量计算,来提升GPU吞吐和利用率,而且对整体服务的延迟长尾能有较好的提升。拆分主要有如下两种: 长prompt拆分为多段,调度到多次迭代批量计算中,只有最后一个段才真正使用生成的token 短prompt和decode token一起拼凑成一次批量计算,但是prompt和decode token在attention计算时分开计算。这种可以充分利用GPU算力。 Chunked Prefill的好处是将访存密集型的decode预算通过和prefill融合,从而转换为算力密集型。将decode效率得到较大提升。一些测试报告显示,chunked prefill调度相对vLLM的continuous batching调度,吞吐提升2.3倍,延迟降低50%。 该项技术已经成熟,已经被引入到vLLM,DeepSpeed等项目中,成为可以配置开启的调度类型。 4.3 PD分离 除了chunked prefill调度优化,另外一种提升GPU利用率的方法就是将prefill和decoding分别运行在不同的GPU集群3 4。这种优化可以针对算力瓶颈不同的两阶段,分配不同的GPU算力,甚至不同型号的GPU,来最大化整个集群的利用率。 典型的架构可以参考MoonCake4架构图,如下所示: 这种调度方法原理上比较直观。在prefill集群只运行prefill计算,将计算完成后得到的KV Cache传输到decoding集群,decoding集群自回归式计算输出token,完成文本生成。整个过程可以发生在一台机器中的多卡之间,通过NVLink完成卡间通讯。也可以发生在多机之间,通过IB或者RDMA网卡来通讯传输KV Cache数据。 为什么这种调度会提升GPU利用率,节省整体的GPU资源呢。可以这样模拟计算一下: 假设prefill的最大吞吐为5.6 RPS(request per second),decoding最大吞吐为10 RPS,那么如果放在一起,平均每卡吞吐最大为min(5.6, 10)/2=2.8 RPS。而将prefill阶段放在两张GPU运行,decoding阶段放在一张GPU,这样平均每卡吞吐最大为min(5.6 * 2, 10)/3=3.3 RPS,相对运行在一张卡,吞吐提升了18%。 更进一步,prefill和decoding可以采用不同的GPU型号,例如prefill集群采用算力较强的GPU,而decoding阶段采用内存带宽更大的型号,整体利用率还可以进一步提升。 PD分离带来的优势有: 可以分别优化首token延迟(TTFT)和token间延迟(ITL),由于PD分离,可以针对P和D分别采用不同的并行策略(TP或者PP),两种延迟之间不互相影响 有效控制ITL的长尾延迟。由于单卡调度Prefill和decoding,会导致prefill和decoding互相抢占GPU资源,导致最坏情况下,输出token发生严重的长尾延迟。 当然,PD分离带来的性能提升,也是建立在保证SLO目标基础上。如果不考虑SLO,PD分离带来的收益并不显著,相反,还会导致运维复杂性提高,P和D集群资源规划更加复杂。这些都是需要考虑的因素。目前PD分离也作为实验功能,进入vLLM等开源项目中。 4.4 多GPU并行推理 对于参数规模在数百B量级以上的大语言模型而言,单GPU的显存大小已经无法承载如此数量的参数和KV Cache了,这必然依赖多GPU共同加载模型,实现并行推理服务。Google在这个领域很早就开始布局5,PaLM的540B大模型就是成功部署在64块TPU v4芯片上。 多GPU并行推理,核心的难题是如何拆分模型,使得推理延迟,MFU(model FLOPS utilization),以及显存使用量同时达到帕累托最优。这个拆分还依赖模型结构和GPU的体系结构特点。例如MHA和MQA下需要考虑不同的分片方法。 模型网络每个阶段,可以采用不同的分片方法,例如在前馈神经网络层(feedforward layer)可以精巧的设计tensor的拆分,通过all-gather操作实现并行推理,在attention层则可以通过batch维度拆分,来降低显存消耗,同时降低卡间通信量,提升MFU。通过这类优化,PaLM 540B大模型的Prefill MFU可以达到76%,Decoding MFU可以达到33%。 ...

February 18, 2025 · Skyan