别再只问一个AI了:多模型交叉验证的常见误区及避坑指南
2026-06-23
2026-06-25 0
关注我~第一时间学习如何更好地搭建AI Agent。
重要的不是我们是否会被AI替代,
而是我们要比被替代的人更懂AI。
前期导览:
LangGraph学习笔记年度总结:以ReAct框架为视角的知识体系全景图
Pi源代码学习-1:Model->Agent->Session 的三层架构概览
大家好,上一期我们梳理了Pi的三层架构,本期开始进入 SDK 实战。
动手之前,先把环境搭好。
首先,确认node版本,要求 >= 22.19.0
然后, cd 切到你打算放项目的文件夹,用 git clone拉取源代码。指令会新建一个pi-mono文件夹,继续 cd进去。

在pi-mono目录下安装依赖并构建项目。

配置模型API key,这里我用的是minimax国内版,使用其他模型的请参考官方文档:

完成以上步骤之后,一般情况下你就可以使用 Pi 了。若想先试 TUI,在仓库根目录( pi-mono )执行 npx pi;Linux/macOS 也可用 ./pi-test.sh。若已全局安装 @earendil-works/pi-coding-agent,则可直接输入 pi。用法类似 Claude Code,在终端里与 Agent 对话即可。
不过本期从 coding-agent 包的 SDK 示例入手,你可以执行以下指令,运行第一个示例 01-minimal.ts:

上一期我们从下往上梳理了 Pi 的三层分工: pi-ai 管模型调用, pi-agent-core 管 agent loop, pi-coding-agent 把它们组装成能直接用的 Coding Agent。三层里对外的总出口,就是 createAgentSession(),官方 SDK 文档也把它称为创建 AgentSession 的主工厂函数。
换言之,只用 createAgentSession() 一行代码、不传参数,我们就能把模型、工具、会话持久化、skills 发现等 Harness 能力全部配齐。想改哪一块,往参数里加就行; examples/sdk/ 从 02 到 13 的示例,都是在这个入口上换不同配置。所以,先从这里入手,能最快看清 Pi 对外提供了什么;
之后继续读源码时,从 session.prompt() 开始往底层探索的话,会依次经过内部的 Agent.prompt()、agent loop,最后在 pi-ai 里调用 streamSimple() 向模型发请求。从这个意义上, createAgentSession()也是我们最合适的入口。
进入示例代码之前,我们需要理解一下 Session 这个概念。在 Pi 里,「Session」其实至少涉及两个层面:
第一个层面,是「一次对话的存档」——Pi 把你在某个项目里跟 Agent 来回聊的整段记录,当作一个 Session 来管理。
通俗来说,这个层面的 Session 就是一份有编号的对话档案: 它有唯一的 sessionId,记录了当时的工作目录( cwd )、创建时间,以及从第一条用户输入到最新一轮回复的全部往来——用户说了什么、模型回了什么、调了哪些工具、中途换过什么模型,都记在里面。你在 TUI 里 /resume 挑一条旧对话继续聊,或在 /fork 里从某条消息分叉出新路线,操作的其实都是某一份这样的档案。
这个层面的session如何保存到我们的设备中,由 SessionManager( packages/coding-agent/src/core/session-manager.ts )负责。
第二个层面,是运行时对象 AgentSession——也就是 createAgentSession() 返回给我们的那个 session对象。
session对象 的定义在 packages/coding-agent/src/core/agent-session.ts,它是Pi的 interactive、print、rpc 等所有运行模式共享的核心抽象。我们调用的 session.prompt()、 session.subscribe()、 session.dispose(),操作的都是这个对象。
它内部持有一个已经配好工具、模型、扩展的 Agent(上一期讲过: session.prompt() 最终会转到 agent.prompt() 去跑 loop );同时还带有 SessionManager、 SettingsManager 等组件,在 loop 之外处理配置读取、事件推送、自动持久化、压缩等 Harness 事务。
换句话说, AgentSession 解决的是如何跟 Agent 打交道 ,而第一个层面那份 JSONL 档案解决的是事后怎么找回来、怎么接着聊 。当然,两个层面间具有非常紧密的关系, createAgentSession() 默认会把它们绑在一起: 运行中的 AgentSession 会通过调用 SessionManager,把对话实时同步进第一个层面的 JSONL 档案。
下面,我们先通过第一个示例代码来理解Pi SDK,尤其是 createAgentSession() 的基本功能。
官方最简示例在 packages/coding-agent/examples/sdk/01-minimal.ts,全文如下:

这段代码就是 Pi SDK 的典型用法。官方 examples/sdk/ 目录下的示例基本都遵循同一套套路:**先 createAgentSession() 创建 session,再使用 session 与 Agent 交互,最后 session.dispose() 释放资源。
这些示例都是一次性 跑完就退出———发一条 prompt、拿到结果、释放资源,进程随即结束。如果想要做成可持续对话的模式,可以尝试在自己的脚本里保持同一个 session 不销毁,循环调用 session.prompt(),每轮输入都会追加到同一段对话历史里即可。
下面我们来具体感受下示例代码。
AgentSession
在这个示例中,我们没有向 createAgentSession() 传递任何参数。这意味着 Pi 组装出来的 AgentSession在模型、工具、工作目录、存档方式等方面,将全部走默认配置。具体来说:

也就是说:你写一行 createAgentSession(),Pi 就按上表帮你配好一套能用的 Coding Agent。当然,我们也可以通过传递参数的方式对模型、工具等进行修改,这就是后面几个示例代码展示的内容了。
示例代码在 session.prompt() 之前写了一个 session.subscribe()。这个 subscribe 是 Pi 对外提供的一条底层事件通道。原理上讲,就是把一个回调函数注册进 AgentSession 的监听器列表,运行过程中 Pi 每产生一个 AgentSessionEvent,就会调用这个回调,把事件对象传进去。
当 Agent 开始工作,就会连续抛出过程事件,比如模型流式输出、工具开始执行、工具返回、本轮结束等。对 TUI 而言,这些事件就是界面更新的数据源:UI不会在问题跑完后再去翻 messages,而是订阅同一条事件流,事件描述「此刻发生了什么」,界面就据此增量刷新。你在终端里看到的对话、工具进度,本质上是 Agent 运行过程通过这条通道实时投射到屏幕上的。
我们来具体看下代码:

上述代码的效果,其实就是在 Agent 运行期间,把模型回复的流式文字实时打到终端上。
Pi 运行过程中会不断调用这个回调;每次调用时,会对接收的事件进行2个条件判断:
event.type === "message_update":这是一条「消息内容正在更新」的事件,说明模型还没把整段话说完,Pi 就在往外推中间状态。
event.assistantMessageEvent.type === "text_delta":在这次更新里,变动的是
正文文字的流式片段
;
event.assistantMessageEvent.delta
就是本次新冒出来的一小段字符串。
只要上述两个条件同时满足,Pi就会把事件里带着的一小段新文字(即 delta ),用 process.stdout.write 续写出来。由于 process.stdout.write 不带换行,所以字会一行行接在同一行后面,看起来就是一个个蹦出来的。
值得一提的是, subscribe 能接的不止流式文字。按官方 examples/sdk/README.md 的写法,你还可以监听 tool_execution_start(工具开始执行 )、 tool_execution_end(工具跑完 )、 agent_end(本轮结束 )等——TUI 的 handleEvent() 也是按这个思路分事件处理的。但在 01-minimal.ts 里,这一步不是必须的:不写 subscribe,后面照样可以向 Agent 发指令并正常跑完;只是脚本里看不到过程输出,你得等 Agent 结束后,再从 session.state.messages 里一次性翻看完整记录。
前面两步准备好之后,真正跟 Agent 对话就靠这一行了:

session.prompt() 是 SDK 里最核心的用法——你把用户输入传进去,Pi 负责剩下的事。
上一期讲过,第二层agent包里的 agent.prompt() 会先把用户输入包装成一条 user 消息,追加到 state.messages,再进入 agent loop 调模型;若模型返回 tool call,就执行工具、把结果写回,并自动再调一轮,直到本轮不再产生工具调用才结束。那是 Agent 的核心工作: 维护内存里的对话状态,跑完 ReAct 式的内层循环。
session.prompt() 并不是另起炉灶。 AgentSession 内部持有一个已经配好的 Agent,也就是说,你调用 session.prompt(),等准备做完,最终还是转到 agent.prompt() 去跑 loop。
差别在于,Session 在 loop 之外又包了一层 Harness。对 Pi 来说, session.prompt() 在把消息交给 Agent 之前,已经替用户做过一轮「发送前处理」——扩展可以拦截或改写输入,skill / 模板命令会先展开,系统提示词会按扩展要求刷新,模型和鉴权也会先校验一遍。
消息进入 loop 之后,内层行为和 agent.prompt() 并无二致;loop 推出来的事件, AgentSession 会接住并往外转:前面注册的 subscribe 能实时收到, SessionManager 也会把消息写入本地档案。loop 跑完也不是到此为止——若需要自动重试、上下文压缩等,Harness 还会继续善后,必要时再驱动 agent.continue() 补跑。
换句话说,** agent.prompt() 解决「单一轮怎么跑」; session.prompt() 解决「发之前做好准备、跑的过程中有人记账和广播、跑完之后有人收尾」**。示例里虽然只写了一行字符串,看起来很简单,其实外面那层 Harness 的脏活累活都已经代劳了。
示例问的是「当前目录有哪些文件」。这类问题 Agent 没法凭空回答,通常会调用默认工具里的 bash(比如跑 ls )去查,再把结果组织成自然语言回复给你。前面如果注册了 subscribe,流式文字和工具进度会边跑边打印;这里加 await,则是等整轮 loop 彻底跑完 再往下走——期间可能经历多轮模型调用和工具往返,但对你而言就是「发一条指令,等它干完」。
prompt() 返回之后,示例没有立刻退出,而是先把对话记录翻出来看一遍,再释放 session:

session.state.messages 是当前内存里的完整对话列表,本质上就是内部 Agent 的 state.messages。跑完一轮 prompt() 之后,这里面通常已经攒下了好几条消息:你的用户输入、模型的助手回复、中间的 tool call 和 tool result 等。示例用 forEach 把它们逐条 console.log 出来,方便你对照「Agent 实际走了哪些步骤」——如果前面没开 subscribe,这里就是一次性查看全程记录的主要方式。
session.dispose() 则是告诉 Pi「这个 session 用完了」。它会断开与内部 Agent 的事件连接、清理监听器,释放本轮占用的资源。示例把它放在 finally 里,是为了无论 prompt() 成功还是抛错,收尾都能执行。值得一提的是,对话记录这件事在运行过程中已经由 SessionManager 做完了, dispose() 并不负责「保存聊天记录」,而是负责把运行时对象干净地关掉。
本期我们从上一期的三层架构落到了 SDK 实战,用官方最简示例 01-minimal.ts 走通了 createAgentSession() 的基本用法:
createAgentSession():一行代码、不传参数,Pi 就按默认配置帮你配好模型、工具、工作目录和会话持久化——这是第三层 Harness 对外的总入口。
2. session.subscribe()
(可选 ):注册回调,接收 Agent 运行过程中的事件流;示例只处理了
text_delta,
让你实时看到模型回复,TUI 的实时展示也是同一套机制。
session.prompt():表面写法与第二层的
agent.prompt()
相近,但多了一层 Harness——发前处理、跑中广播与存档、跑后善后,内层 loop 仍由
Agent
执行。
session.dispose():负责干净释放运行时资源。
跑通这个示例之后,我们就掌握了 Pi SDK 的标准套路:创建 session → 按需订阅 → 发指令 → 收尾释放 。 examples/sdk/ 里从 02 到 13 的后续示例,都是在这个骨架上替换模型、工具、存档方式等配置——这些定制化我们后面再研究。
好了,以上就是本期的主要内容,希望对大家有帮助,喜欢的朋友别忘了点赞、收藏、转发~祝大家玩得开心。
—— END——
往期精华:
1.LangGraph教程
LangGraph学习笔记年度总结:以ReAct框架为视角的知识体系全景图
2.OpenDeepResearch源码学习
3.COZE教程
零基础搞定!萌新的 Coze 开源版保姆级本地部署指南
AI工作流编排手把手指南之一:Coze智能体的创建与基本设置
AI工作流编排手把手指南之二:Coze智能体的插件添加与调用
AI工作流编排手把手指南之三:Coze智能体的工作流
Agent | 工作流编排指南4:萌新友好的Coze选择器节点原理及配置教程
Agent | 工作流编排指南5:长文扩写自由 — Coze循环节点用法详解
Coze工作流编排指南6:聊天陪伴类智能体基本工作流详解-快来和玛奇玛小姐姐谈心吧~
PPT自由!Coze工作流 X iSlide插件-小白也能看懂的节点参数配置原理详解
4.MCP探索
Excel-MCP应用 | 自动提取图片数据到Excel的极简工作流手把手教程
markitdown-mcp联动Obsidian-mcp | 一个极简知识管理工作流
【15合1神器】不会代码也能做高级图表!这个MCP工具让我工作效率翻了不止三倍!
【效率翻倍】Obsidian自动待办清单实现:MCP联动Prompt保姆级教程(萌新3分钟上手 )
萌新靠MCP实现RPA、爬虫自由?playwright-mcp实操案例分享!
高德、彩云MCP全体验:让Cherry Studio化身私人小助理的喂饭版指南!
5.Prompt设计
干货分享 | Prompt设计心法 - 如何3步做到清晰表达需求?
打工人看了流泪的Prompt设计原理,如何用老板思维让AI一次听懂需求?
不会Prompt还敢说自己会用DeepSeek?别怕!10分钟让你成为提示大神!