第 06 章

看不见的就调不动 · 部署 + 监控 + OS 视角

7 节 · Cloud + Docker + CI/CD baseline + Web stack 轻度 + OS/网络/GPU minimum + Logs/Metrics/Traces 三件套 + 5 类 LLM-specific 可观测信号。MIT 6.S081 / Stanford CS144 列深 dive。

约 1.5 小时
Stage 3 收官 · 看不见的就调不动

这一章是 Stage 3 工程基底的最后一章,约 1.5 小时,主体 Mode A,MIT 6.S081 加 Stanford CS144 列章末可选深 dive。读完后,你能读懂一个真实 LLM 产品的 Dockerfile 加 GitHub Actions yaml 加一次 LLM call 的 trace 截图加 GPU memory budget,production 现场的核心可见性都对你打开。

为什么这章 Mode B 视频列为可选深 dive 而不是必看:MIT 6.S081(OS)和 Stanford CS144(网络)是顶级大学完整公开课,每个 30-50 小时量级,超出 foundations baseline。学完本章 1.5 小时 Mode A 已经足够 baseline,只有想往 systems engineer 深方向走的学生需要去看那些课。

5 大块内容覆盖:Cloud + Docker + CI/CD baseline,Web stack 轻度 + Webhook,OS / 网络 / GPU minimum(只教 AI 工程每天用到的部分),Observability 三件套(logs / metrics / traces),LLM-specific 5 类可观测信号。读完这一章,Stage 3 圆满收官,你具备把 LLM prototype 推到 production 且能 debug 的完整工程基底。

一个真实问题 · 你 Agent 上线两周,凌晨 3 点出问题了

让我用一个 production 现场开篇。假设你的 Agent 系统上线两周,运行平稳。凌晨 3 点 PagerDuty 报警,p99 latency 暴增,从 800ms 跳到 12 秒,持续 5 分钟。你睡眼惺忪起来,打开监控 dashboard,要在 10 分钟内回答几个问题:哪个组件慢了?是 LLM provider 那边挂了吗?是某个用户突然狂打 API 触发了 rate limit?是 RAG 的 vector DB 查询变慢了?还是网络抖动?

你能不能在 10 分钟内回答这些问题,完全取决于你 day 1 上线时部署了什么 observability 基础设施。如果 day 1 没装 trace 平台,你打不开任何过去 5 分钟的请求详情;没装 structured log,你 grep 几 GB 文本日志找原因;没分 metric,你看不到 latency 是哪个组件慢的。事实是,部署不是把代码推到云上就完事,部署是把代码推到云上加让代码在云上出问题时你看得见。后者是这一章的核心。

DeepSeek 全栈 JD verbatim 写明 "熟练运用 Profiling 和可观测性工具分析与定位复杂系统问题",Observability 是 4 大实验室 explicit baseline。这不是 senior 工程师才需要的能力,是 entry-level 入场卡。为什么它现在成了入场卡而不是 senior 专属,放到 Ch 1 时间线上看就清楚了:2022 之前 LLM 还停在 demo,没人需要监控一个跑不起规模的玩具;2024-2025 agent 自主跑多步、用户上到亿级,系统出问题的代价从我自己调不出来升级成凌晨烧爆账单、1% 用户静默流失,observability 才从奢侈品变成 day-1 必需。这一章我们从最基础的 Cloud / Docker 部署开始,一路走到 trace 平台加 5 类 LLM-specific 信号,让你 day 1 上线就装好,凌晨 3 点出问题时你看得见。


1. Cloud + Docker + CI/CD baseline

1.1 三大 Cloud · GCP / AWS / Azure 的定位

行业三家 cloud 主导:AWS / GCP / Azure。AI 领域分布上,AWS 市场份额最大,综合能力最强,Bedrock 是它的多 model gateway;GCP 是 DeepMind 加 Vertex AI 主场,TPU 独家,整合 Gemini;Azure 是 OpenAI 独家 hosted partner,企业 Microsoft 生态深。DeepSeek 加阿里加腾讯加字节等中国 cloud 在国内 AI 主导,如果你做 China-market 产品,这一层是必修;欧美市场主要还是 AWS / GCP / Azure 三家。

每家 cloud 都提供四类基础能力:Compute(VM / 容器 / Serverless)、Storage(对象存储加 DB 加缓存)、Network(LB / CDN / VPN)、AI services(hosted model API 加 vector DB 加 embedding)。这四类能力构成 cloud 的产品矩阵,你的 LLM app 跑哪家都行,关键看 latency 加 cost 加 provider lock-in 三个维度。如果你重度用 OpenAI,Azure 略有优势(同区域延迟低);重度用 Anthropic,AWS Bedrock 整合更好。这意味着 cloud 选择更多是跟 LLM provider 协同的考虑,而不是 cloud 本身好坏。

1.2 Serverless vs Container vs VM · 选哪个

部署 LLM app 有三种主流形态。Serverless function(如 AWS Lambda / GCP Cloud Run / Vercel)适合短任务加按请求计费;Container service(如 AWS ECS / GCP Cloud Run / Fly.io)适合中等任务加需要 background worker;Full VM(如 EC2 / GCE)适合大 GPU 任务加持续重负载。

LLM 应用层(调外部 LLM API,不自托管模型)绝大多数走 Container service,例如 Cloud Run / Fly.io / Railway 等。原因有三:第一,比 Serverless 灵活,可以有长连接加 WebSocket 加长任务,而 Serverless function 通常 timeout 短(15 分钟 max),不适合 30 分钟的 research agent;第二,比 VM 便宜,按 CPU/Mem 计费加自动扩缩容,你的 Agent 凌晨没人用时自动缩到 0 实例就不花钱;第三,比 Kubernetes 简单,不用配 cluster,Cloud Run / Fly.io / Railway 这种 managed container service 帮你管所有 infra。只有自托管 model 推理才需要 GPU VM,foundations 不教自托管,那归 #2 系统设计加 #4 模型设计。

1.3 Docker · 把应用打包成可复制的环境

Docker 解决一个古老问题,我电脑跑通你电脑不能跑。具体说,Docker 把你的 app 加依赖加系统库打包成一个 image,这个 image 在任何机器跑都一样。本地、staging、production 跑同一个 image,行为完全一致。这种环境一致性是 production 部署的基础,没有 Docker 这一层抽象,跨环境迁移每次都要重新解决依赖冲突。

一个 LLM Python app 的最小 Dockerfile:

dockerfile
FROM python:3.12-slim

WORKDIR /app

# 1. 装依赖(先 copy requirements,利用 layer cache)
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# 2. 复制源代码
COPY . .

# 3. 暴露端口 + 启动
EXPOSE 8000
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]

跑一遍:

bash
docker build -t my-llm-app .          # 构建 image
docker run -p 8000:8000 -e ANTHROPIC_API_KEY=$KEY my-llm-app  # 跑

这个 Dockerfile 里藏了几条 production 经验。第一,指令顺序影响 build 速度,把不变的(依赖)放前面、把变的(代码)放后面,layer cache 命中率最大化;如果你把 COPY . . 放在 RUN pip install 之前,每次代码改一行都要重新装所有依赖,build 时间从几秒变几分钟。第二,选 slim 镜像不选 full,python:3.12-slim 是 Debian-based 精简版镜像(约 150MB),不要用 full python:3.12(约 1GB),镜像越小,pull 加启动越快,服务扩缩容更敏捷。第三,永远用环境变量secret(API key / DB password),绝对不要硬编码进 Dockerfile,后者会随 image 泄漏给所有能拉镜像的人。

1.4 Environment variable + Secret · 配置和密钥

Environment variable 是部署时注入的配置(API key / 数据库地址 / 模型名 / feature flag),不在代码里、不在 git 里。Secret 是敏感的环境变量,需要加密存储加限制访问。这两层概念区分清楚:env var 是统称,secret 是其中需要保护的那部分。Production 标配分三层:Local dev 用 .env 文件(必须在 .gitignore 里)加 dotenv 加载;Cloud production 用 Secret Manager(GCP Secret Manager / AWS Secrets Manager / HashiCorp Vault);CI/CD 用平台自带的 secret 存储(GitHub Actions Secrets / Vercel env vars)。每一层都有专门的工具防止 secret 泄漏到代码或日志里。

有一种典型反面做法值得明确反对:把 ANTHROPIC_API_KEY="sk-..." 写进代码、commit 到 git、存在公开文档里。这是 production 灾难的 #1 起源,GitHub 公开 repo 一旦泄漏 API key,几小时内自动 scraper 就会扫到并狂打,你醒来发现几万美元账单。Day 1 就要建立 secret 永远不进 git 的肌肉记忆,这是 production 工程素养的第一道门槛。

1.5 CI/CD · git push → 自动测试 + 部署

CI(Continuous Integration)即每次 commit 或 PR 自动跑 test 加 lint 加 type check,保证 main 分支永远绿。CD(Continuous Deployment / Delivery)即每次 merge to main 自动部署到 staging 或 production,人不点按钮。这两件事合起来叫 CI/CD,是现代 production 系统的标配自动化层。为什么这件事在 production 重要?因为人会忘、人会犯错、人凌晨 3 点状态差。CI/CD 把 test 加 deploy 这两件高频操作自动化加标准化,消除人因 bug。回到例子,如果你手动部署,某次忘记跑测试就上线,引入 bug 用户报错,这种错每个团队每年都发生几次;CI/CD 让这种错从根本上不可能发生,因为没人能跳过自动化步骤。

最简单 GitHub Actions 例:

yaml
# .github/workflows/ci.yml
name: CI
on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with: { python-version: "3.12" }
      - run: pip install -r requirements.txt
      - run: pytest
      - run: ruff check .
      - run: mypy .

合并到 main 自动部署(用 Fly.io 为例):

yaml
# .github/workflows/deploy.yml
name: Deploy
on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: superfly/flyctl-actions/setup-flyctl@master
      - run: flyctl deploy
        env:
          FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}

Production 经验有四条要记住:CI 必须快,超过 5 分钟开发者会绕过,失去价值;CI 失败必须 block merge,否则人会忽略;Deploy 必须可 rollback,上线坏了一键回退;生产部署必须有 alert,deploy 后 5 分钟监控错误率突增就自动回滚。这四条每一条都对应一次真实的 production 事故教训沉淀下来,看似常识但很多团队没做齐。

1.6 Dev / Staging / Prod 三段式

任何 production 系统都该有三个环境。Dev 是你的电脑或本地,随便改加随便坏;Staging 是 production-like 但用户访问不到,新代码先这里跑过;Production 是真实用户在用的环境,最严格的部署门槛。三段式不是冗余,是把代码可能坏这件事限制在 dev 加 staging,让 production 只承接验证过的代码。为什么必须有 staging?因为 LLM provider integration 在 dev 跑通,不代表 production 跑通,并发加长时间加真实 traffic 可能暴露 dev 看不到的问题。Staging 跑 production-like 流量是上线前最后的安全网,缺了这一层,bug 就直接发生在用户身上。配套的纪律是每个环境一份单独 secret,绝不共用,staging 用 prod 的 OpenAI key 这种事是 production 灾难,会让 staging 的错误调用计入 prod 账单,也让 prod key 在更宽松的环境里多了泄漏面。


2. Web stack 轻度 + Webhook

部署搞定。下一层要回答的问题是,用户怎么跟你的 LLM 系统交互?这一节目标不是教你成为前端工程师,是让你看到一个 LLM app 的前后端代码,能 grounded 出前端在干嘛、后端在干嘛、接口怎么连。不要求精通 React 或 Next.js 或 Vue。

典型 LLM app 前后端职责分工:

谁负责干嘛
前端UI 渲染 / 用户输入收集 / streaming 文本显示 / 错误展示
后端LLM API 调用 / 工具执行 / 状态管理 / Auth / Rate limit

接口形式(对应 Ch 3 三种 API 模式):

text
前端                            后端
 │                              │
 ├──POST /chat (message)─────→│
 │                              ├── 调 LLM(可能 streaming)
 │                              ├── 调工具(可能多次)
 │←──SSE stream (token chunks)─┤
 │                              │
 │ 渲染到 UI                    │

或对长任务(用 Ch 5 学的 async + queue 架构):

text
前端                            后端
 │                              │
 ├──POST /research (question)→│
 │                              ├── 推入 queue(见 Ch 5 · queue)
 │←──{ job_id: "xxx" }─────────┤
 │                              ├── Worker 跑 30 分钟
 │ WebSocket 连接               │
 ├──ws://progress/xxx──────────→│
 │←──{ step: 3, msg: "..." }───┤
 │                              ├── 任务完成
 │←──{ status: "done", ... }────┤

读到这种代码你应该立刻识别 sync 还是 async、streaming 还是 batched、状态在哪,这都是 Ch 5 学的。这就是把工程基底应用到具体场景的体感,看到一段架构图,脑子里立刻能对应到前面章节学过的 mental model。

2.1 Webhook · 外部事件主动通知你

最后一个 web 概念是 Webhook,它是外部系统在事件发生时主动 POST 到你的 endpoint,和 polling(你主动定时查)相反。LLM 应用常见场景有三类:Anthropic Batches 任务完成后 webhook 通知你回去取结果;Stripe 支付完成后 webhook 通知你给用户开通服务;GitHub PR 创建后 webhook 通知你的 Agent 自动 review。这种被动接收的模式比主动 poll 省资源,也让事件响应更及时。

python
@app.post("/webhook/batch-complete")
async def batch_complete(payload: dict):
    batch_id = payload["batch_id"]
    # 1. 验证签名(防伪)
    if not verify_signature(payload):
        return 403
    # 2. 处理事件
    await process_batch_result(batch_id)
    return {"status": "ok"}

Production 关键是 webhook 必须验签,否则任何人发 POST 到你 endpoint 都能伪造事件。Stripe 加 GitHub 这种成熟 webhook 提供方都签 payload,你用 shared secret 验。验签是 webhook handler 的第一行代码,不是可选项,跳过它意味着你的系统对任何能 POST 到 endpoint 的人都开放控制权,这在 production 里相当于把家门钥匙挂在门外。


3. OS / 网络 / GPU minimum

部署加 Web 框架搞定。但 production 系统的真正底层是操作系统加网络协议加硬件,vibe coder 通常没系统学过这层。

本节范围 · 只教 AI 工程每天用到的部分

DeepSeek 全栈 JD verbatim 写明 "深刻理解计算机组成、操作系统、计算机网络等核心原理",其他 3 实验室多 implicit。vibe coder 通常没系统学过 OS / 计算机网络 / 计算机组成,但 AI 工程每天都用一部分。本节只教 AI 工程每天用到的部分,约 15 个核心概念,够你 grounded 讨论 production 系统的 latency / memory / network 决策。完整 CS 学留给未来 "基础学科 · CS" 路书。

3.1 Process / Thread / Async · 三种并发模型

操作系统让你同时干多件事有三种主要机制。Process(进程)是独立的运行单元,有自己的内存空间,互不干扰,代价是启动慢加切换贵;Thread(线程)是同一进程内的执行流,共享内存,代价是同步问题加 Python GIL 限制;Async event loop(异步事件循环)是单线程,但能在等 IO 时切到别的任务,轻量加高并发,但只对 IO-bound 适用。这三种机制不是互斥,production 系统经常混用,但 LLM app 有一个清晰的默认选择。对 LLM 应用,绝大多数选 async event loop。原因有一个简单事实:LLM API call 是 IO-bound,你的 app 99% 时间在等 LLM 返回 token,1% 时间在算事情。Async 让你等 LLM A 返回的时候,切去处理 LLM B 的请求,单线程也能高并发。Python 的 asyncio、Node.js 原生、Go goroutine 都是这条路。这意味着你写 LLM app 不需要去深究多线程同步那一堆复杂概念(锁、condition variable、deadlock),async 这一层就够 cover 99% 场景。

Python 还有一个并发特性值得专门点出:Python 有 GIL(Global Interpreter Lock),多线程也只有一个 thread 能跑 Python 代码,所以 thread 在 Python 里只对 IO-bound 有帮助,CPU-bound 必须 multi-process。LLM app 几乎全 IO-bound,async 是首选。这条规则在 production 反复验证:几乎所有 Python LLM 应用都走 asyncio,只有少数本地推理 / 数据处理场景才需要 multi-process。

3.2 Stack vs Heap · 内存的两块

程序运行时内存分两大块。Stack(栈)装函数调用加局部变量加调用链,自动管理(进函数 push,出函数 pop),快但小(默认几 MB);Heap(堆)装动态分配的对象(Python 里几乎所有东西都在 heap),GC 管理,慢但大。对 vibe coder 来说,Python / TS / JS 几乎都是 heap 主导的语言,你写 obj = {...} 这个 object 就在 heap 上,stack 只有函数调用的元数据。这一层有两个反直觉点会真实影响你的 Agent 代码。第一,递归无限可能 stack overflow,Python 默认 recursion limit 约 1000,所以 Agent loop 不要写成递归(像 process(state) -> process(new_state) 这种形态),要写成 while 循环,否则深的 trajectory 直接爆 stack。第二,Reference vs Value 陷阱,Python / TS / JS 里 primitive(int / str / bool)是 value,object / list / dict 是 reference;Agent 系统里,你把 state 传给一个子函数,子函数 in-place 修改后,主流程的 state 被悄悄改了。Best practice 是子函数收到 mutable 参数时,先 deepcopy 再改,或者明确返回新对象。

3.3 GPU 视角 · LLM inference 的 memory budget

你跑外部 LLM API 时,不需要管 GPU memory,provider 帮你管。但理解这层让你懂为什么 long context 慢加贵。一次 LLM inference 的 GPU memory 分布:

部分占什么
Model weights模型参数本身(几百 GB 量级,具体不公开)
KV cache已经算过的 token 的 attention 中间结果(与 context 长度成正比)
Activation当前 forward pass 的中间张量(与 batch size 加 context 成正比)

事实是,long context 的 cost 不只是 token 数乘以单价,是 GPU memory 占用加 KV cache 计算开销。Provider 实际上面对内部限制(GPU memory 上限加调度复杂度),所以对长 context 收高价是有 cost rationale 的,不是纯涨价。这也是为什么 streaming 的 TTFT 后会加速生成,KV cache 已经算好,后续 token 只需要 forward 一遍模型加 attention 到 cache,所以 token-by-token 出来很快。理解这一层让你在 Ch 3 学的 token 经济学有了物理 grounding,不再是抽象的价格表,而是对应到 GPU 上真实发生的事。

3.4 HTTP / TCP / IP · 网络分层

网络是分层的,从上到下:

text
Application:  HTTP / WebSocket / gRPC  ← 你的代码在这层
Transport:    TCP / UDP                ← 保证传输可靠
Network:      IP                       ← 路由数据包
Link:         以太网 / WiFi             ← 物理传输

对 LLM app 你只需要懂 HTTP 加 WebSocket(应用层),但有几个逃不开的概念。HTTP 是 stateless,每个 request 独立(和 LLM 一样,见 Ch 5),要让系统记得用户,得用 cookie 或 token 携带 state。HTTPS 等于 HTTP 加 TLS 加密,生产必须 HTTPS,不只是为了加密,也为了 integrity(防中间人篡改)加 server identity(防钓鱼)。Keep-alive 是 TCP 连接复用,同一 connection 跑多个 HTTP request,LLM streaming 必须 keep-alive,否则每个 token 都开新连接。WebSocket 是双向长连接,Agent 进度通知加 chat UI 必用。这五个概念合起来,就 cover 了 LLM app 网络层 99% 的日常场景。

3.5 Latency numbers · 数量级直觉

Jeff Dean 经典图(2012,有些数字现在略快但量级不变):

操作耗时类比
L1 cache 访问约 0.5 ns1 单位
L2 cache约 7 ns14× L1
主内存(RAM)访问约 100 ns200× L1
SSD 随机读 1 KB约 150 μs30 万× L1
网络往返 同机房约 500 μs100 万× L1
HDD 寻道约 10 ms2 千万× L1
跨大西洋网络约 150 ms3 亿× L1
LLM TTFT(Claude Sonnet)约 400 ms约 8 亿× L1
大 LLM 推理(2 万 token output)约 30 s约 600 亿× L1

数量级直觉很重要:L1 cache 比网络往返快 100 万倍,LLM call 是程序里最慢的一段之一,只比 HDD 慢一个数量级。这两组数字一对比,你立刻知道程序的瓶颈在哪。这指导你做三类设计决策:任何 LLM call 必须 cache 能 cache 的(prompt cache / embedding cache / 结果 cache);跨网络调用必须 keep-alive,不要每次新连接;不要为了省 10 μs 优化代码,那是 LLM 时间的 0.0001%。

举一个 production 反例。一个 vibe coder 每次调 LLM 前新建 OpenAI client object,这本身小于 1 ms 可以忽略,但他的 LLM call 没 prompt cache,每次多花 10× token 钱,这才是优化重点。直觉数字让你不在小事上浪费时间,而集中精力优化真正的瓶颈,这是 production engineer 跟 vibe coder 在工程判断力上的核心差距之一。


4. Observability · Logs / Metrics / Traces 三件套

回到开篇凌晨 3 点的场景。你能不能 10 分钟内 debug 出问题,完全取决于 day 1 装了什么 observability。

为什么 LLM observability 比传统系统难

传统后端 observability 三件套(logs / metrics / traces)基本够。LLM 系统多了一层模型决策不可见,你看到模型返回了什么,但不知道它为什么这么返回。错了不知道是 prompt 错 / context 错 / model 错 / 工具错。DeepSeek 全栈 JD verbatim 写明 "熟练运用 Profiling 和可观测性工具分析与定位复杂系统问题",Observability 是 4/4 lab explicit 的 baseline。LLM observability 必须加几个 LLM-specific 信号(下一节展开)。

4.1 Logs · 系统在什么时候做了什么

Logs 是系统运行过程中产生的事件记录,最基础也最重要的 observability primitive。但 log 不是随便写的,production 关键要求是 structured logging(结构化日志),不要写 print(...) 这种乱字符串,要写 JSON:

python
import structlog

log = structlog.get_logger()

# Bad
print(f"User {user_id} asked: {question}")

# Good — structured
log.info("user_question",
         user_id=user_id,
         question=question,
         session_id=session_id,
         timestamp=now())

为什么 structured 关键?Production 你不是肉眼读 log,是查询加聚合,"过去 1 小时所有 user_question event,filter user_id=X" 要 SQL-like 查询。String log 你只能 grep,JSON log 你能 filter 加 group_by 加 count。这个差距在凌晨 3 点 debug 时是分钟 vs 小时的差距,grep 几 GB 文本日志你 30 分钟还没看到关键事件,JSON 日志加 SQL 查询 30 秒出结果。工具上有 Loki 加 Grafana / Datadog Logs / CloudWatch / Sentry / 自家 Elasticsearch 等可选,Day 1 选一个装上,所有 log 走 structured 格式。

4.2 Metrics · 量化的系统状态

Metrics 是数字化的 observation,通常是时间序列(每秒或每分钟取一次)。经典三类:Counter 只增,例如请求数 / 错误数 / 处理过的 token 数,比如 "过去 24h 我们处理了多少 LLM call";Gauge 可增可减,例如当前并发数 / 队列长度 / 内存占用,比如 "现在有多少 worker 在跑";Histogram 是分布,例如 latency 分位数 / response size 分布 / cost per call 分布,比如 "p99 latency 是多少"。这三类合起来 cover production 系统所有量化观测需求。

python
# Prometheus 风格
from prometheus_client import Counter, Histogram

llm_calls_total = Counter("llm_calls_total", ["model", "status"])
llm_latency_seconds = Histogram("llm_latency_seconds", ["model"])

def call_llm(prompt):
    start = time.time()
    try:
        result = client.messages.create(...)
        llm_calls_total.labels(model="sonnet-4-6", status="success").inc()
        llm_latency_seconds.labels(model="sonnet-4-6").observe(time.time() - start)
        return result
    except Exception:
        llm_calls_total.labels(model="sonnet-4-6", status="error").inc()
        raise

这一节最重要的反直觉是看分位数,不看平均值。LLM 系统 latency 长尾极重,平均 800ms 可能 p99 是 15s,用户体感由 p99 决定,不由平均决定。如果你只看 average,你的 dashboard 永远绿,但 1% 用户在凌晨 3 点等了 15 秒后流失,这种问题平均值永远看不到。回到开篇的报警场景,那个 "p99 跳到 12 秒" 的信号如果你只看 mean 是发现不了的,mean 可能还在 1 秒以内,但 1% 用户已经在体验灾难。

4.3 Traces · 一次请求穿过所有系统的路径

Trace 是一次请求从入口到出口的完整执行路径记录,包括穿过的每一段(span)加每段耗时加每段输入输出。对 LLM 系统,一次 trace 通常长这样:

text
[Trace: research_question_42]
├ Span: api_handler (10ms)
├ Span: load_user_context (50ms)
├ Span: agent_loop (Total: 23.5s)
│  ├ Span: llm_call (model=opus) (3.2s)
│  ├ Span: tool_search_web (1.1s)
│  ├ Span: llm_call (model=opus) (3.5s)
│  ├ Span: tool_read_url (4.2s)
│  ├ Span: llm_call (model=opus) (8.1s)
│  └ Span: tool_summarize (3.4s)
└ Span: format_response (5ms)

读这一张 trace 你立刻看到几件事:总耗时 23.6 秒(用户体感太慢),llm_call (model=opus) 一共 14.8 秒是大头,优化方向是换小模型 / 减少 LLM 轮次 / 并行调工具。没 trace 你看不到这些,你只看到整个请求 23.6 秒这个总数,不知道哪个组件慢。事实是,trace 是把请求时间打开成组件级 timeline 的 X 光机,这是其他 observability primitive 给不了的能力。

工具上有几个选项:OpenTelemetry 是开放标准;Langfuse / LangSmith / Helicone 是 LLM-specific 平台,给你 LLM 友好的 trace UI;Datadog APM / Honeycomb 是通用 trace 平台。Trace 是 logs 加 metrics 之后最高级的 observability primitive,它带来看穿一次请求的能力。production LLM 系统不上 trace 是技术债,等问题来了再补,你的历史数据没了,debug 难度乘以 10。这是为什么所有成熟 LLM 团队都把 trace 平台列为 day 1 必装。


5. LLM-specific 可观测信号 · 5 类

传统三件套之上,LLM 系统必须多记 5 类信号。

5.1 完整 prompt + completion

每次 LLM call,完整的 prompt 输入加完整的 completion 输出都得存。否则会有四种 production 痛点同时出现:用户报 "AI 回答错了" 你无法 reproduce;改 prompt 后回归你无法对比前后;监管 audit 你交不出;后续 fine-tuning 训练数据你没有。每一种痛点单独看都不是 day 1 致命,但合起来构成 LLM 系统的核心信息黑洞,不解决以后想做任何系统化的质量改进都没数据基础。存哪也有讲究。LLM call 的 prompt 加 completion 数据量大但不常查,适合 cold storage(S3 / GCS)加索引(Elasticsearch / Vector DB)。这种分层存储让 cost 可控的同时还保留了 debug 时拉数据的能力,不要图省事直接塞进主 DB,几天就把 DB 撑爆。

5.2 Token usage 累积

每次 LLM call 都记录 input_tokens 加 output_tokens 加 cache_hit_tokens,聚合到 metric 层成为每个用户、每个 feature、每个 prompt template 的累积 token 与 cost。这一层数据在 production LLM 系统里直接关系到生死,因为 LLM 系统的 #1 production 风险是 cost 失控,一个 bug 让循环失控,几小时就能跑掉几千美元。这正对应 Ch 1 第 3 节 列的那堵成本的墙:推理模型想得越久越贵、大规模部署的经济性仍是真问题,而 token 监控就是你在产品这一侧守住这堵墙的第一道防线。Token usage 监控加 alert 是必备防线。当某用户单日 token 突增 10x 时立刻告警,你能在烧爆账单前介入。回到开篇 Agent 上线两周的场景,如果没装这套监控,某个用户的 prompt 触发了循环 bug,你等到月底账单来才发现已经多花了几万美元,中间两周完全无感。装了监控,凌晨告警那一瞬间就能停掉问题用户,损失控制在几十美元。

5.3 Latency 分位数(p50 / p95 / p99)

每个 LLM call 记录 TTFT(回到 Ch 3 学过的)加 Total latency,按模型、按 prompt template、按 user_id 分组。看 p99 比看 mean 重要 10 倍,p99 latency 高意味着 1% 用户体感差到流失;p99 latency 暴增通常意味着 provider 那边有 incident,需要立即 fallback。回到开篇凌晨 3 点的场景,那个 "p99 暴增到 12 秒" 的报警,就是从这一类监控来的。没有这一层分位数视图,你只能看到一个温和的 mean,problem 已经在发生但 dashboard 一片绿。

5.4 Failure attribution · 哪一步失败 + 是不是 retryable

LLM call 失败时,记录到分类粒度:HTTP 状态码(429 / 500 / 504 等)、Provider 名(OpenAI / Anthropic / DeepSeek)、Model 名、是不是 retryable(回到 Ch 5 学的分类)、Retry 次数加是否最终成功。这五个维度每一个都对应一类 production 决策:状态码告诉你失败类型,provider 告诉你哪家挂了,model 告诉你哪个模型有问题,retryable 决定要不要自动重试,retry 成功率告诉你 fallback 策略是否有效。为什么关键?你需要 alert "Anthropic 429 率突然 10× 上升"(rate limit 即将爆),alert "504 timeout 突然增多"(provider incident)。Aggregated failure attribution 是 production LLM 系统的 dashboard 核心面板,没有这一面板,出故障你看到的只是一个 "错误率上升",但不知道是哪家 provider 的哪类错误,fallback 都不知道往哪 fallback。

5.5 Tool call 完整链路

每次 tool call 记录 Tool name 加 input 加 output(或 error)加耗时加是否成功加是否 retry 过。Agent 系统的失败 90% 是 tool 层失败(网络问题 / 权限问题 / 参数错),tool call trace 是 debug Agent 的核心 input。没有这一层数据,Agent 行为对你完全是黑盒,模型说 "我用 search 找到了 X",但 search 实际返回了什么、是不是 timeout、参数对不对你一概不知。工具友好的组合是 OpenTelemetry 加 Langfuse 这一对,前者是开放标准,后者提供 LLM 友好 UI 把 trajectory 可视化成一张 timeline。这种 timeline 视图让你点开任何一次 Agent 跑批,就能看到完整的工具调用序列加每一步的参数加每一步的耗时,debug 时间从翻 log 找半天缩短到看 timeline 立刻定位。

Profiling 工具入门

DeepSeek 全栈 JD 显式要求 Profiling 工具熟练。Profiling 是比 observability 更深一层,分析为什么慢、贵、占内存。

工具适用
Python: cProfileCPU profiling for Python
Python: py-spySampling profiler · 不改代码可用 · Production 抓现场
Python: memory-profiler内存使用追踪
Node: clinic.js / 0xNode profiling
Browser: Chrome DevTools Performance前端 profiling
LLM-specific: Langfuse / LangSmithLLM call timing + cost

最小起步是至少 Langfuse 或类似 LLM trace 平台从 day 1 装上,这一件事的边际成本极低(几十行 SDK 集成),但带来的可见性回报是 production debug 能力的数量级跃迁。其他 profiling 工具按需引入,真遇到 CPU bottleneck 再装 cProfile,真遇到内存问题再装 memory-profiler,不必 day 1 全装。


6. 综合 · 一个 production LLM app 完整 setup

把这一章所有内容合起来,一个 production LLM app 的完整 setup 长这样:

text
[ Developer 本地 dev ] (Docker compose 跑本地 + Postgres + Redis)
   │
   │ git push
   ↓
[ GitHub ]
   ├── PR opened → CI(test + lint + types) → 等 review
   └── merge to main → CD trigger
       ↓
[ GitHub Actions ]
   ├── Build Docker image
   ├── Push to Container Registry
   └── Deploy to Cloud Run / Fly.io / Railway
       │
       ↓
[ Production environment ]
   ├── Container service (auto-scale to traffic)
   ├── Postgres (managed · RDS / Supabase / Neon)
   ├── Redis (managed · Upstash / Elasticache)
   ├── Secret Manager(API keys)
   ├── Object storage(prompt + completion log archive)
   └── Observability:
       ├── structlog → Datadog Logs
       ├── OpenTelemetry → Datadog APM
       └── Langfuse(LLM-specific trace + token + cost)

这一套 day-1 上线 1-2 周,之后你的迭代速度从改代码后手动部署的形态变成改代码后自动上线,同时凌晨 3 点出问题时你看得见。这两个收益都是工程基底投资的复利,前期装好一次,后续每次部署、每次 debug 都受益。

Observability 反 pattern · 6 类

review 时看到以下就是 P1 技术债:

  1. 没有 structured log,全是 print(),grep 找信息,无法聚合
  2. 没记完整 prompt + completion,用户报 bug 无法 reproduce
  3. 只看 mean latency,不看 p99,长尾用户体感盲区
  4. 没有 token usage dashboard,cost 失控才发现
  5. 没有 failure attribution,错误率上升不知道哪个 provider 的问题
  6. trace 平台 day 1 不上,等问题来了再上,历史数据缺失,debug 难度乘以 10

每一条都是 production 灾难的种子。本章学完后,你看一个 LLM 系统的 codebase 应该立刻扫一遍这 6 条,凡缺都是 P1 技术债。这种 review 速度跟具体语言无关,只跟你脑子里有没有这 6 条 checklist 有关,装了这一套 checklist 你 review 任何 LLM codebase 都能在 5 分钟内说出技术债清单。


收官 · 你现在在哪 + 下一站

回头看你这一章走了什么。你拿到了 Cloud + Docker + CI/CD baseline,三大 cloud 定位、三种部署形态选择、Dockerfile 最佳实践、CI/CD 自动化、Dev/Staging/Prod 三段式。你拿到了 Web stack 轻度加 Webhook,前后端职责分工、接口形式(REST / Streaming / WebSocket)、Webhook 验签。你拿到了 OS / 网络 / GPU minimum,Process/Thread/Async、Stack/Heap、GPU memory、HTTP-TCP-IP 分层、Latency 数量级直觉,这是 AI 工程每天用到的 CS 基础切片,够你 grounded 讨论 production 决策。你拿到了 Observability 三件套,Logs / Metrics / Traces 各自做什么加 structured 是基础。你拿到了 LLM-specific 5 类信号,完整 prompt+completion、token usage、latency 分位数、failure attribution、tool call trace,这是 LLM 系统超越传统 web 系统的额外可见性要求。

Stage 3 圆满收官。你现在具备把 LLM prototype 推到 production 且能 debug 的完整工程基底,这是 vibe coder 到 production engineer 转型的工程技术部分。下一章(Ch 7 · foundations 收官)进入 Stage 4 系统判断力,包括 Evals 加 Code Taste 加 Meta-skill。我们把工程基底升级为看一个方案能问出关键问题的判断能力。读完 Ch 7,foundations 路书结束,你具备面对 LLM 系统能做 trade-off 判断、参与 code/design review、把跨工具用 LLM 作 meta-skill 的完整 baseline。

你完成了 foundations 的第 5 个心流断点,Stage 3 圆满收官。合上书,去练:拿你之前写过的 Agent 项目,补一个 Langfuse 集成看看 trace 长什么样;在 Dockerfile 里实践一遍 layer cache 优化;用 structured log 替换所有 print。这种把抽象 mental model 应用到具体项目的练习,是工程基底真正长出来的过程。下次回来,带着扎实的 production engineering 直觉进 Ch 7 收官。


本章术语速查

部署基线:Cloud(AWS / GCP / Azure) · Serverless / Container / VM 三选 · Docker 打包应用 · Dockerfile / Image / Container 三件套 · Layer Cache(Dockerfile 指令顺序影响 build 速度)· Environment Variable / Secret 配置和密钥 · Secret Manager 加密存储 secret · CI / CD 自动测试 / 部署 · Dev / Staging / Prod 三段环境 · Rollback 一键回退 · Webhook 外部事件主动 POST · Signature Verification 防伪签名验证

OS / 网络 / GPU:Process / Thread / Async event loop 三种并发 · GIL(Python 全局锁)· Worker / Worker Pool 后台任务处理 · Backpressure(queue 满时拒绝)· Stack vs Heap 调用链 vs 动态 · Reference vs Value 引用 vs 值 · Deepcopy 深拷贝 · KV Cache / Activation(LLM 推理中间结果)· HTTP / HTTPS / Keep-alive / WebSocket 网络应用层 · Port / Firewall 端口 / 防火墙 · L1 cache / L2 / RAM / SSD 内存层级 · Latency budget 数量级直觉

Observability:Observability 看清系统行为 · Logs / Structured Logging 事件 / JSON 格式 · Metrics 量化状态(Counter / Gauge / Histogram)· Traces / Span 请求路径 / 子操作 · OpenTelemetry 开放标准 · Langfuse / LangSmith / Helicone LLM-specific 平台 · Datadog / Honeycomb / Grafana 通用 · Profiling 深度分析 · TTFT / p99 LLM-specific latency · Failure Attribution 故障归因 · Token Usage 每 call 消耗

完整词汇见 术语表 /glossary


参考文献

Mode B · 必看视频

无。本章主体是工程实战,无单一合格视频 cover。

Mode B · 可选深 dive 视频(超出 foundations baseline · 想往 systems 深方向走再看)

为什么这两个标为可选深 dive:它们是顶级大学完整公开课(每个 30-50 hr),超出 foundations baseline 太多。学完本节 OS / 网络 minimum 1.5 hr 已够 baseline。想深度走 systems engineer 方向再去看,那是 #2 系统设计的内容范围。

Mode A · 引用出处

  • DeepSeek 全栈 JD · "深刻理解计算机组成、操作系统、计算机网络等核心原理" 加 "熟练运用 Profiling 和可观测性工具" · 收录 teaching-system/research/foundations/synthesis.md
  • Jeff Dean · "Numbers Everyone Should Know" · 2012 · Latency 数量级直觉来源
  • Anthropic / OpenAI / DeepSeek · 各家 Batch API doc · Webhook signature 验证标准来源

深 dive 资源(可选 · 静态资源)

OS / 系统编程:

网络:

部署 / DevOps:

Observability:

  • Charity Majors et al. · "Observability Engineering" · O'Reilly · 现代 observability 圣经
  • Honeycomb Engineering Blog · observability 思想源头

下一章 · Stage 4 起点

下一章(Ch 7 · foundations 收官)进入 Stage 4 系统判断力,包括 Evals 加 Code Taste 加 Meta-skill。把工程基底升级为看一个方案能问出关键问题的判断能力。