第 23 章 · 运行时架构设计

Harness 锚点|分层骨架——pi 的 packages/* 如何切层,支撑判别清单全部 10 项。

从“我电脑上能跑”到“给一万个卖家用”

那个跨境卖家的选品 Agent 越做越顺手,他动了个念头:干脆做成一个 SaaS,卖给同行的卖家们用。可真要动手,他立刻撞上一堵墙——原来那个在自己笔记本上跑得欢的脚本,一旦要同时服务成千上万个卖家,处处都是窟窿:换个更便宜的模型得改一堆业务代码;想加个网页版发现 Agent 逻辑和命令行搅在一起拆不开;某个卖家报“结果不对”却完全没法复现……

“在我电脑上能跑”和“能作为产品稳定服务成千上万用户”之间,隔着一条鸿沟。生产环境要求的是 Demo 不关心的东西:清晰的分层(哪块归哪块管)、可替换(换个模型不用重写业务)、可观测(出问题能查)、可控(成本、安全、权限)。

这一章我们先看最根本的一环——架构分层(layered architecture):一个能上生产的 Agent 运行时,应该怎么切分职责。

分层:让每层只管一件事

好的架构核心就一个字:分层。把复杂系统切成若干层,每层职责单一、通过清晰接口交互,层与层之间只靠约定好的接口通信,不互相伸手掏内部。对 Agent 运行时,一个通用的分层是四层:

  • 模型层(Model Layer):与各家 LLM 打交道——统一接口、处理重试、计费、抹平多 provider 差异。它对上层只暴露一个“给我 prompt、还我 completion”的接口,至于底下接的是 OpenAI 还是 Anthropic,上层不关心。
  • 运行时层(Runtime Layer):Agent 的核心循环(ReAct)、工具调用、状态管理、上下文/记忆。这是“Agent 之所以是 Agent”的地方。
  • 编排层(Orchestration Layer):多 Agent 的协调、工作流、交接(handoff)。单 Agent 用不到,多 Agent 系统才需要。
  • 接口层(Interface Layer):怎么和外界交互——CLI、Web、RPC、API。同一个 Agent,可以长出多张脸。

分层带来什么:换一层不动其他层

分层最实在的好处,是关注点分离(separation of concerns) 带来的可替换性:换一层不影响其他层

  • 换个模型 provider?只动模型层——业务逻辑纹丝不动(这正是第 34 章“模型分层”的前提)。
  • 加个 Web 界面?只动接口层——运行时层完全复用。
  • 想给多 Agent 换种编排策略?只动编排层——工具和模型都不用碰。

这背后是一条老到发亮的软件工程原则:依赖倒置(dependency inversion)——上层依赖的是下层的接口,而非下层的实现。模型层承诺“我提供一个统一的 LLM 调用接口”,运行时层就只管照这个接口用,底下换成谁家的模型都无所谓。没有这层解耦,换模型就意味着改遍全身;有了它,换模型只是改个配置。

用 pi 实现:pi 的包结构就是分层

pi 的架构是这套分层的一个真实样本——它的 packages 目录几乎就是照着层切的:

pi 包职责
@earendil-works/pi-ai模型层统一多 provider LLM API、重试分类、定价
@earendil-works/pi-agent-core运行时层agent loop、工具调用、状态管理
@earendil-works/pi-orchestrator编排层多 agent 编排(实验性)
@earendil-works/pi-coding-agent接口层交互式编码 Agent CLI
@earendil-works/pi-tui接口层终端 UI 渲染

这种切分带来的实际收益,你在前面的章节已经反复见到:

  • 模型可替换(第 34 章):因为有独立的 pi-ai 层,换模型只是换个配置。
  • 能力可扩展(第 8 章):因为运行时层暴露了生命周期事件,扩展(extension)能挂载而不用改核心。
  • 多种接口:同一个运行时,既能做 CLI,也能通过 RPC 远程驱动(第 24 章)。

量级有别:有的 harness 是单语言、单进程的轻量分层(如 pi),有的用多语言、多进程把编排/执行/推理拆到不同运行时——同样是分层思想,量级不同。理解了分层原则,你能看懂任何一种。

一项 AI 业务能力的参考架构

技术分层解决“代码如何组织”,业务能力架构还要解决“谁触发、需要什么状态、可以做什么、谁承担结果”。一个能进入生产的 Agent,至少要把下面六层打通:

核心职责典型组件
业务触发层定义经营目标、触发事件、业务对象与结果负责人订单事件、线索进入、库存预警、人工发起
流程与状态层表达正常路径、异常分支、暂停恢复和人机交接状态机、DAG、审批流、任务队列
Agent / Harness 层管理推理循环、上下文、工具、记忆、编排和停止pi、Agno、LangGraph 或自研 Runtime
企业上下文层按权限装配必要的数据、规则、知识与历史RAG、数据服务、规则服务、身份权限
工具与集成层把判断转成可校验、可审计的系统动作ERP、CRM、OMS、MCP、API、浏览器
治理与运营层贯穿全链路记录质量、风险、成本和业务结果Tracing、评测、策略、预算、告警、反馈回流

这六层形成的最小完整闭环可以浓缩成:

业务触发 → 获取必要上下文 → Agent 判断 → 工具执行 → 高风险/异常转人工 → 记录业务结果 → 进入评测与复盘

第一版不需要把每层都建成公共平台。先围绕一个高价值、高频、边界清晰的流程,把这条链路完整跑通;当第二、第三个场景出现相同需求时,再把上下文连接、工具注册、策略和评测抽成公共能力。这样架构随着真实复用生长,而不是脱离业务提前建设。

分层之外:生产还需要什么

分层是骨架,但一个生产系统还要在这骨架上加几样东西,后面几章会逐一展开:

  • 可观测性(observability):日志、追踪、成本统计(第 25 章)。
  • 预算控制(budget control):别让一个失控的 Agent 烧光预算(第 26 章)。
  • 安全边界(security boundary):不信任的代码要隔离(第 28 章)。
  • 状态持久化(persistence):崩溃可恢复(第 12 章已讲)。

本章你只需记住一个顺序:先有清晰的分层,这些才好加。如果模型调用、业务逻辑、界面渲染搅成一团,你想加预算控制都不知道该往哪儿插——因为没有一个清晰的“模型层”来统一拦截调用。

真实案例:pi 生态的分层

pi 生态本身就是“分层 + 各司其职”的一个漂亮示例。它分成三块:

组件角色
pi(引擎/CLI)Agent 运行时、多 LLM、工具调用、会话管理
pi-app(Web + macOS)Web 界面、桌面壳、可选远程访问
你的工作流用 CLI、浏览器或桌面 App——都连到同一个 Agent

关键在于三者共享同一份数据契约:~/.pi/agent/ 目录(auth.json 凭据、settings.json 设置、sessions/ 会话)。pi-app 明确规定“不自建存储、不复制凭据”——你在 CLI 里 /login,浏览器端立刻能用同一个 provider;你在 App 里建的会话,终端 pi 在同一目录下也列得出来。

这带来一句极精炼的分层原则,值得记住:

能力来自 pi,表达来自 pi-web。

也就是说:Agent 的能力(循环、工具、会话)只在引擎层实现一次;界面层(Web/桌面)只负责表达,绝不重新实现一套 Agent 逻辑。这正是本章“换一层不影响其他层”的现实印证——同一个引擎,既能长出 CLI,也能长出 Web 和 macOS App。假如哪天要接入手机,新加的也只是一层“表达”,引擎层一行都不用改。

动手看看

打开 pi 的 packages/ 目录,对照上面那张表,看看每个包的 package.jsondependencies 都依赖了谁——你会发现依赖是单向朝下的:接口层依赖运行时层,运行时层依赖模型层,反过来没有。这就是分层最直观的证据:上层认识下层,下层不认识上层。

再做个思考实验:如果你要给这个 Agent 加一个“企业微信机器人”入口,你觉得该改哪个包、新建哪个包?(提示:它是一张新的“脸”,属于接口层——运行时层和模型层应该一行都不用动。)

实战中的几个坑

坑一:业务逻辑写进了模型层。

  • 现象:想换个模型,却发现 provider 调用那块混着一堆选品业务判断,牵一发动全身。
  • 原因:没守住层的边界,把上层的事塞进了下层。
  • 对策:模型层只做“统一调用 + 重试 + 计费”,业务判断一律上移到运行时层。

坑二:过度分层,简单需求也套四层。

  • 现象:一个单 Agent 小工具,也硬塞进“编排层”,绕来绕去、样板代码一大堆。
  • 原因:把“分层”当成越多越好,忽略了量级。
  • 对策:分层数量随复杂度走——单 Agent 用不上编排层就别加;原则相同,量级要匹配。

坑三:接口层里偷偷实现了 Agent 逻辑。

  • 现象:Web 端为了“快”,自己写了一段调模型的逻辑,和引擎层重复且很快不一致。
  • 原因:违背了“能力来自引擎、表达来自界面”。
  • 对策:接口层只负责表达,任何 Agent 能力都回到运行时层实现一次、各接口共用。

坑四:层间用具体实现而非接口耦合。

  • 现象:运行时层直接 import 了某家 provider 的 SDK,换模型要改运行时代码。
  • 原因:上层依赖了下层的实现而非接口,违反依赖倒置。
  • 对策:让下层暴露稳定接口,上层只依赖接口——换实现时上层无感。

对比:其他框架

四家框架各有自己的分层方式,本质都是“让每层只管一件事”,差别在层次划分与耦合度。

框架架构分层怎么做特点
LangGraph模型层、链/图(编排)层、部署层清晰分离,Platform 管部署、LangSmith 管可观测分层最显式,代价是层次多、样板多
CrewAI主要暴露 Crew/Agent/Task 三层,底层运行时藏起来抽象层次高、上手快,但分层是框架定死的、插自定义逻辑空间小
PydanticAI不强加架构分层,鼓励用依赖注入和类型“像写普通 Python 一样”组织分层由你的工程习惯决定,适合融入既有应用
AgnoAgentOS 作运行时+监控层,上层 Agent/Team/Workflow,下层 Memory/Knowledge/Tools分层清晰且“电池全含”,适合快速搭完整应用
pipackages/* 一目了然:pi-ai/pi-agent-core/pi-orchestrator/coding-agent 各司一层单语言轻量分层,你在其上构建而非被框架框住

一句话点破:分层的本质都是“关注点分离 + 换一层不动其他层”,各家差别只在层是框架替你定死的、还是你自己划的——理解了原则,原书 Shannon 那种多语言重型三层你也一样看得懂。

小结

  1. Demo 到生产的鸿沟,靠清晰的架构分层来跨:模型层、运行时层、编排层、接口层,每层职责单一、只靠接口通信。
  2. 分层的核心收益是“换一层不影响其他层”——背后是依赖倒置:上层依赖下层的接口而非实现,于是模型可替换、能力可扩展、接口可多样。
  3. pi 的 packages/* 就是这套分层的真实样本;pi 生态“能力来自 pi、表达来自 pi-web”是“不重复实现能力”的现实印证;原书 Shannon 是更重的多语言三层——原则相同,量级不同
  4. 分层是骨架,生产还需在其上加可观测、预算、安全、持久化——而“先有分层”是这些能干净落地的前提。
  5. 别过度分层:层数随复杂度走,单 Agent 用不上编排层就别硬加。

有了分层骨架,下一章解决生产的关键难题之一:怎么让 Agent 可靠地远程运行——pi 的 RPC 与远程驱动。