《AI Agent 高级编程》10.1 统一可观测:三框架 trace 归一 OTel
pydantic-ai 原生就吐 OTel span,smolagents 与 minagent 却只有各自的内部轨迹。本节在装了 opentelemetry-sdk 1.44.0 的环境里真跑:把三种来源的 trace 归一到一张中间表示,再统一 emit 成 GenAI 语义约定的 OTel span 树,并给出三框架 span 字段到 OTel 属性的逐项映射表。
category
pydantic-ai 原生就吐 OTel span,smolagents 与 minagent 却只有各自的内部轨迹。本节在装了 opentelemetry-sdk 1.44.0 的环境里真跑:把三种来源的 trace 归一到一张中间表示,再统一 emit 成 GenAI 语义约定的 OTel span 树,并给出三框架 span 字段到 OTel 属性的逐项映射表。
灰度让新旧两版代码同时在跑,于是 schema 变更不能一删了事。本节讲 expand–contract 五阶段迁移法,并在本机 Postgres 17.11 上用 psycopg 真跑一遍:加新列、双写、回填、切读、删旧列,每阶段查询验证新旧两列一致,另演示 DDL 锁表与 CREATE INDEX CONCURRENTLY。
Agent 服务的发布对象不只是代码,提示词、模型 ID、工具集、检索参数都是可发布的配置,改提示词的风险不低于改代码。本节设计按流量比例的灰度与自动回滚条件,真跑一个灰度路由器:新版正常时放量、新版劣化时按成功率/成本/P95 三个条件触发一键回滚,并演示影子流量。
本节把 ResearchCopilot 的 CI 串成一条分阶段流水线:静态检查 → 单元测试 → 评测质量门 → 构建镜像,按「快先跑、贵后跑」排序。真跑 compileall、pytest、评测夹具回放与 docker build,打印每段真实退出码,给出一键脚本与 GitHub Actions 形态。
本节是 TS 落点一:用 TypeScript 写一个消费 17.2 SSE 协议的最小客户端——fetch 拿到 ReadableStream,逐块解码、按空行切帧、解析 event:/data: 行,把 token/tool/step 事件渲染成可读输出。全部代码只用 Node 26 的可擦除语法,node client.ts 直跑、tsc --noEmit 过检,在线与离线两种方式真跑。
本节把 minagent 跨出进程边界:用 FastAPI 与 uvicorn 把引擎暴露成 HTTP 服务,POST /run 一次性返回、POST /stream 用 StreamingResponse 以 SSE 逐事件推送,把 7.1 的 Event 映射成 event:/data: 行。我们真起本地服务打两个端点,贴出真实响应与 SSE 事件流,并讲 OpenAPI 的意义。
本节把 minagent 从「一堆模块」整理成「一个库」:用 __init__.py 收敛出稳定的公开 API 面,用 __all__ 与下划线前缀划清公开契约与内部实现的边界,并定下 SemVer 版本策略——破坏性变更何时允许、哪些名字属于契约。最后用 import minagent as ma 真跑一段只碰公开面的稳定性示例。
训模型不是默认答案。本节给出一张「何时该训、何时调提示」的成本收益决策清单,把调提示、换模型、加工具、微调、RL 五种干预按成本与收益排开,说清每种手段的前置条件与失效场景;并配一个可运行的决策打分小脚本,对「冷启动无标注」与「有 5k 可验证轨迹」两种场景真跑排序,帮你在动手训之前先算清楚这笔账。
SFT 教会模型「格式」,RL 才教会它「取舍」。本节讲清从监督微调到强化学习的完整流程,聚焦工具使用与多步推理:对比偏好奖励与可验证奖励,说明为什么「工具调用成功、引用可回溯」这类可自动判分的信号特别适合 Agent,并给出一个真跑的离线策略打分例子——给定若干轨迹与奖励函数,算出每条的分数并选出最优。
Agent 的强化学习不是给分类任务换个 loss。本节把 Agent 的多步交互形式化成轨迹(状态、动作、奖励序列),讲清奖励设计与信用分配——到底是哪一步的功劳。配一段可运行的轨迹与奖励数据结构,以及一个按折扣回报做信用归因、再按总回报给轨迹排序的离线小例子,真跑贴输出;全程不训练模型,不编造任何实验曲线。
16.2 把服务编排起来了,但容器编排还有三件「看不见却致命」的事:配置怎么注入、健康检查怎么区分 liveness 与 readiness、收到 SIGTERM 后怎么优雅退出。本节讲清用环境变量注入配置、两条健康线各自的取舍、以及「停止接新请求→等在途 run 收尾→关连接池→退出」四步。用 uvicorn 0.54.0 + lifespan 真发 SIGTERM,贴出完整日志序列与参数表。
上一节做出了 ResearchCopilot 的镜像,但一个镜像撑不起一个服务——它还要数据库、缓存和后台 worker。本节写 docker-compose.yml 编排这四个服务,讲清 depends_on 不等于「依赖就绪」(须配 healthcheck)、同一镜像靠 command 扮演 api/worker 两种角色。真跑 config 与 ps 起栈验证。
本节是 TS 落点三的上半场:把 ResearchCopilot 打成 Python + Node 双栈镜像。用多阶段构建分别产出 TS 前端产物与 Python 依赖,只把产物拷进运行时镜像;再叠加 .dockerignore、--no-cache-dir、slim 基础镜像与 digest 固定四个瘦身杠杆。Dockerfile 在本机真构建,贴出镜像体积、分层与容器内验证的真实输出。
本节把 minagent 推进到对比实验:在同一套评测集上改一个变量看分数怎么动——把 max_steps 从 8 收到 2、在提示里加一句引用约束,各跑一遍并输出成功率、平均步数、平均成本的对比表。核心是控制变量、固定评测集、多指标并看。
本节把 minagent 推进到评测层:定义 Case 与 Scorer(工具命中率、答案关键点、引用可回溯率、步数、成本),实现 evaluate 返回 Report,并用假模型脚本化六条用例的正确与错误行为。真跑评测打印每条得分与汇总,作为 Agent 的离线回归网。
本节把 minagent 推进到可回归的测试层:用 FakeModel 与 pytest 写断言轨迹的测试,断言 Agent 调了哪些工具、调用顺序、参数与最终答复,并演示如何用脚本注入错误、让工具抛异常来制造失败路径。六条用例真跑 pytest -q,输出 6 passed。
8.1 的 A2A 端是 Python 写的,可真实团队里另一半服务可能是 TypeScript。协议的价值正在于此:只要双方说同一种「线上语言」,语言就不重要。本节用 TypeScript(Node 26,只用可擦除语法)实现一个 A2A 端,与 8.1 的 Python 端跨语言真跑一次双向协作:TS 服务端被 Python 客户端调用,TS 客户端又反过来调用 Python 服务端。
上一节把 A2A 跑通了,可很多人第一反应是:这跟 MCP 有什么区别?答案是它们解决的根本不是同一件事——MCP 是 Agent 连工具,A2A 是 Agent 连 Agent。本节用一张对照表把「解决什么、谁连谁、典型对象、有无状态、怎么发现、什么时候用哪个」逐项讲清,再真跑 MCP 与 A2A 两端的对象与调用,给出一张「该用哪个」的决策表与两者的组合形态。
前七章讲的是单个 Agent 怎么用框架写得更好,可真实系统往往有多个 Agent。它们怎么互相发现、怎么把活儿交出去、怎么拿回结果?这正是 A2A(Agent2Agent)协议要解决的。本节先讲清 Agent Card、Task、Message、Artifact 四个核心对象与任务状态机,再用 Python 标准库起一个 A2A 服务端、由客户端发现并委派任务,把一次真实的任务往返贴出来。
本节讲清审计日志与普通日志的区别:普通日志可丢可采样,审计日志必须完整、必须能证明没被改过。用追加式加哈希链实现审计日志,每条记录带前一条的哈希,改任何一条都会让链断掉。真跑:写 5 条记录、校验链完整、篡改中间一条再校验,打印「第 N 条校验失败」,并给出审计字段表与保留策略表。