夜雨聆风学习资料网

ARTICLE · 1089109

解构Pi源码学习Agent系列(一)认识Pi的架构方式

解构Pi源码学习Agent系列(一)认识Pi的架构方式

我是一名普通的程序员,在AI时代真的是没有任何的竞争优势了。AI Coding已经抹平了技术差距,技术人员和非技术人员都可以写代码构建产品,也没有前端和后端的岗位了,只有全干的岗位。

我逼迫自己学习AI,赶紧转型,大模型我是接触不到了,也不是我能接触到的,能接触到的就只有Agent了。所以我准备系统的学习一下Agent,我准备把我所有的学习结果全部输出,只有把输入的知识完整的输出,才算是完整的掌握了,这也是费曼学习方法。我准备把Pi的源码系统的学习一下,通过学习Pi的源码,来了解Agent的原理。

为什么选择了Pi,因为Pi足够极简,功能比较少,coding agent默认提供read,write,bash,edit四个编程工具,也支持扩展其他工具,非常适合通过源码来了解Agent的原理。这篇文章是解构Pi源码系列的第一篇,先了解一下Pi的架构方式,以及Pi每个包之间的关系。了解完了架构和每个包之间的关系之后,我会逐个对每个包进行源码级别的解构。

本文分析的Pi源码版本是0.85.1。Pi的仓库是一直在更新的,也许你看到的官方仓库源码结构跟我这里分析的不一样,那就是因为代码库更新了,为了保证学习体验,我会把这篇文章分析的源码放在后台,你只需要私信Pi就能获取源码了。

Pi仓库中有哪些包

Pi的项目是monorepo的管理方式,在我分析的时候Pi一共有11个包,但是都没有全部正式发布到Pi CLI里。Pi CLI以coding-agent为主体,运行时使用到了agent,ai,tui和chord,telemetry则被ai和agent间接使用。

client,server,protocol对应的远程会话方案目前还处于实验阶段,不属于Pi CLI的运行时依赖。session-backends/sqlite-node 是可选的sqlite会话存储实现,正式CLI默认使用JSONL文件保存会话,evals则是开发阶段的评测工具。

我们在终端交互的其实是coding-agent这一层,coding-agent是最终实现的产品层。coding-agent把tui,agent,ai,chord等模块组合起来,实现了最终的PI CLI。

每个包的作用

图中ai指向telemetry的箭头表示包之间的依赖,不表示模型调用后必须经过的执行步骤。

我们要先了解每个包的作用,这样才能更好的理解整个Pi的架构和运行机制,我将逐个介绍每个包的功能和它在整个系统中的角色。

pi-chord

先来说下pi-chord,它是一个微内核运行时,chord不依赖Pi的其他子包,但并不是所有包都依赖它。源码中没有任何关于LLM,Agent,Prompt,TUI的业务逻辑。它是一个高度通用的系统基础库,设计目标就是完全脱离Pi,可以用于构建其他复杂的跨进程,跨环境的应用体系。

chord是为了解决,多个模块之间的通信和同步状态的问题,比如你写了一个插件,你只需要关注插件本身就可以了,插件与TUI和Server之间的通信由chord来管理。很多人就说chord不就是来管理通信的吗?远不止于此,chord还可以管理依赖。

//定义一个服务层export const Models = defineService<ModelsService>("pi.models");//提供一个实现env.provide(Models, implementation);//使用这个服务层const models = env.use(Models);

如果你在coding-agent中定义了一个服务层models,你在其他模块中想要使用这个服务层,只需要在模块中引入这个服务层,然后就可以使用了。chord就是解决了多个模块之间的依赖关系,所以这里有点类似依赖注入。chord运行的时候,它会先启动provider,然后启动consumer,consumer会先等待provider启动完成。结束服务的时候会先销毁consumer,然后销毁provider,这就是为什么把chord称为微内核运行时。

chord另外一个作用就是管理状态,需要跨模块共享状态时,chord可以把状态更新发布给本地或远程的使用者,以上就是chord的大致功能。

pi-ai

统一的大模型接入网关,统一的流式输出API,抹平不同大模型之间的差异,统一Tool Calling函数定义规范。统一处理思考链与跨厂商模型会话的消息转换。

pi-ai负责统一模型调用和流式响应,也提供模型管理,认证与用量统计,它不负责Agent循环或执行命令等工具。

pi-tui

独立的终端用户界面框架,通过差量渲染和同步输出减少字符闪烁,主要用于接收用户输入和显示信息,它的内部没有任何大模型相关的业务逻辑,也可以单独作为框架使用,用来开发其他CLI终端应用。

pi-telemetry

Agent在执行任务时,不只是说把事情做完就可以了,还要知道经历了什么,花了多长时间,消耗了多少token。哪一步卡住了,有没有报错,这些都是需要检测的,这就是telemetry的作用。

在这份源码中,使用pi-telemetry的模块定义了以下可记录的字段(具体能否得到某个值还取决于调用过程和模型提供商)。

大模型交互层

  • 耗时:首个流式数据块的耗时,流式总耗时和chunk块的总数量。

  •  资源与成本:输入token,输出token,以及推理token和缓存读写命中的token。

  •  结果与终止原因:HTTP 状态码、stop_reason(正常结束、达到长度限制、触发工具、报错或中止等)。

Agent调用工具与执行层

  • 一个任务持续了多长时间。

  • 工具调用监控:工具(read/edit/bash)的名称以及执行是否出错。

  • 记忆压缩:会话压缩的耗时、触发原因和执行状态。

可以把它看作高级日志,它是结构化的树状结构数据,传统日志就像在纸上写流水账,而遥测可以把相关操作串成树状记录,帮助分析各步骤。

pi-agent

pi-agent-core的核心职责是,通用智能体决策与调度核心,

  • 通过agent-loop协调大模型响应流与工具调用交替进行。

  • 维护通用的消息模型AgentMessage(user, assistant, toolResult)

  • 提供Steering即时插话机制与 Follow-up 任务追加队列。

  • 提供上层AgentHarness 运行容器,Skills加载与会话压缩逻辑抽象。

pi-agent-core是不包含任何的编程工具(read/edit/bash),所以被定位为通用智能体的内核,还可以用于客服、问答、自动化测试等。

sqlite-node

sqlite-node是基于nodejs22的持久化会话树存储实现,主要用来管理会话分支。

实验性的远程通信

Pi CLI已经提供pi --mode rpc模式,外部程序可以通过标准输入发送JSON命令,并从标准输出接收响应和事件,这种模式适合把Pi作为子进程接入其他程序。先说下RPC是什么,RPC是Remote Procedure Call的缩写,中文意思就是远程过程调用。它是一种调用方式,允许客户端向另一个进程或服务发出请求,并获得响应,就像本地调用一样,而不需要了解底层网络通信的细节。

仓库中的pi-protocol,pi-client,pi-server是另一套实验性的远程通信方案,用在让客户端连接独立运行的服务端。

一共包含三个包

  • pi-protocol:定义客户端与服务端之间的消息格式,基于CBOR编码实现紧凑的数据封装。CBOR是一种二进制数据编码格式。

  • pi-client: 提供建立连接,发送请求和订阅服务状态的客户端能力。它是供其他程序使用的SDK,注意一下,不是桌面应用的客户端。

  •  pi-server:提供服务端的会话路由和连接管理能力,支持多个客户端连接同一会话,断线后的重新连接需要客户端显式发起。

目前这三个包用于开发阶段的远程集成,不属于正式Pi CLI的运行时依赖;上面提到的`pi --mode rpc`不依赖它们。

应用产品层与评测

coding-agent:应用产品层,就是我们常用的Pi CLI,它把pi-tui,pi-ai,pi-agent,pi-chord,pi-telemetry等模块组合起来,实现了最终的Pi CLI。里面包含编码工具(read.ts、edit.ts、bash.ts、write.ts)。

pi-evals:开发阶段的评测工具,主要用于评估Agent的性能和效果。

总结

以上就是Pi的架构方式和每个包的作用,下一篇我会介绍一下agent-loop的实现原理,以及agent-loop是如何协调大模型响应流与工具调用交替进行的。

相关学习资料