Cell - Ta Channels
Ta 自带的 pi 包(默认开启,可在 Web 客户端的 设置 → 包 关闭):智能体与频道。由三部分组成,都不改 Ta 的核心:
- 服务端(
pi.host,web-src/host.ts→host/index.mjs):ta serve 在本进程里运行它一次。团队、消息和待回答的队列都由它保管和推进,转发在服务端进行,关掉页面也继续。 - pi 扩展(
pi.extensions,web-src/extension.ts→extension/index.mjs):给 Ta 的会话加上团队工具,经pi.events取得服务端的团队。 - Web 扩展(
pi.web,web-src/index.tsx→web/index.js):只负责显示和发起修改。
概念
- 智能体:一个普通的 Ta 会话,加上名字和职责(作为
appendSystemPrompt),会话名就是智能体的名字,会话属于@celljs/ta-channels分组(对话列表里折成一行)。每个智能体有自己的卡通头像(web-src/faces.tsx,16 种形状和配色,记在face里;新建时尽量不和已有的重样,没有face的旧数据按 id 分配),会眨眼,回答时左右张望;Ta 自己仍是 Cell logo。会话文件一打开就记下,下次从文件重新打开。智能体的会话里没有团队工具。 - 频道:成员(智能体,第一个是负责人)和消息。消息发给其中
@到的成员;智能体的回答贴回频道,它在回答里@谁就再交给谁。每个智能体一次只处理一条消息。用户可以把成员设为免打扰(muted):它在这个频道里什么都不回答,被@也不回答;负责人免打扰时,没人被@的消息交给下一位没有免打扰的成员。 - 防循环(
ChannelsUtil.limit):智能体之间接力最多 3 跳,一条消息引出的回答最多 8 条、每个智能体最多 2 条;被截断时,等这一轮结束后在频道里追加一行淡色说明("已到交接上限,等你继续"),每轮只追加一次,post_to_channel的返回里也带上这句。 - 工具:ta serve 里 Ta 的每个会话(Web 页面里的对话,以及连到 ta serve 的其他客户端)都有
create_agent、create_channel、add_to_channel、post_to_channel、list_channels。用户说一个目标("组个团队做落地页")时,Ta 建好智能体和频道、把目标贴进去,等各成员回答完再回复用户;频道缺某个角色时,Ta 用add_to_channel把已有的智能体加进来(加在已有成员之后,负责人不变);开着的页面在侧栏给这个频道标一个绿点,打开后消失。中途停止 Ta 只是不再等待,智能体会在频道里接着回答。 - 界面:侧栏顶部的"智能体"标签(和"对话"标签并排)把分身、频道和智能体放在一起(分身是 ta-web 内置的页面,开启后列在最上面,导航栏里不再单独出现):标签右边是搜索框和 +。+ → 新建智能体在主区域打开一个草稿页:先给一个没人用的名字和头像,顶栏点名字可以改名、从 16 个头像里挑,侧栏顶部多一行"还没开工";输入框发出第一条消息时才真正建好智能体(职责是通用的,具体做什么写在这条消息里),随即切到它的会话并发出这条消息。+ → 新建频道是一个弹窗:名称、搜索并勾选智能体(1–12 个,先勾的是负责人)、创建;频道行是负责人在前、下一位成员在后的叠放头像,名称旁标出负责人,第二行是最近一条消息或谁正在回答;智能体按工作目录分组,第二行是"空闲"或正在哪个频道回答,右侧是最近一次发言的时间。点智能体打开它的会话(
openAgent请求,服务端打开或找到会话后由pi.switchSession显示为当前对话)。智能体行悬停(触屏上常显)出现 …,菜单里是:停止(中止它正在进行的回答)、重新开始(开新会话)、重命名、职责(编辑系统提示词里的角色,下一个会话生效)、移至…(换工作目录,在新目录开新会话)、复制工作目录、导出对话(Markdown,去掉频道的上下文块)、加入频道、删除(退出所在频道,只剩它的频道一并删除,会话文件保留);靠近底部时菜单向上展开。频道是主区域的一个页面(不进右侧工作台),以群聊显示:别人的消息在左边带头像和名字,用户的在右边,连续发言合成一组,停顿久了插一行时间;顶栏是频道名和成员头像,点头像在右侧工作台打开"频道成员"(members,location: 'panel'的视图,跟着当前频道):列出成员,负责人带标记,正在回答的标绿;每行点开是智能体的会话,悬停(触屏上常显)出现设为负责人、免打扰、重开会话(忘掉之前的对话,下次回答时开新会话,旧会话仍在对话列表里)和移出频道(至少留一位);顶部"添加成员"换成可搜索的智能体列表,勾选后添加,列表底部的"新建智能体"打开新建智能体的草稿页;底部是删除频道(再确认一次,消息一并删除,正在进行的回答不再贴出)。输入框就是对话的输入框(pi.ui.Composer),@列出成员,+ 里可以点名成员或添加成员(打开频道成员的添加列表);消息用对话的 Markdown 渲染(pi.ui.Markdown),点名的成员加粗。
数据与转发
数据存在服务器上的 <agentDir>/extension-data/@celljs/ta-channels.json,只有服务端写;每个频道保留最近 200 条消息。
| 键 | 内容 |
|---|---|
team | 智能体与频道 |
messages/<频道> | 频道的消息 |
queue | 还欠着的回答(job:哪个智能体回答哪条消息、第几跳、属于哪一轮)和各轮的计数 |
working | 正在回答的智能体 |
focus | Ta 用工具建过或发过消息的频道,页面据此在侧栏标出 |
requests/<id> / results/<id> | 页面发起的请求(新建智能体、新建频道、添加频道成员、设负责人 / 免打扰 / 移出成员、删除频道、重开智能体的会话、修改或删除智能体、以用户身份发消息、打开智能体的会话)及服务端的回复;页面只写 requests/* |
页面通过 pi.storage.onChange 得知变化后重新读取。每个回答先作为 job 记进 queue,回答贴进频道后才移除。ta serve 重启后接着处理剩下的 job:已经有回答(频道里有该智能体对这条消息的回复)的 job 不会再问,已经交出去的消息也不会再交一次,所以已处理的消息不会重复转发。重启时正在回答、还没有贴出的那一条,会再问一次。
限制
- 只有 ta serve 有团队:单独运行
ta(CLI,没有 ta serve)时,团队工具仍然存在,但调用时会返回"需要 ta serve"的说明,不会创建或转发任何东西。包的服务端没能启动时,工具会说明启动失败,请看 ta serve 的日志。 - 没有
@任何人的消息(用户或 Ta 发的)交给负责人,即频道的第一个没有免打扰的成员;智能体的回答没有@谁时,接力到此结束。 - 页面的修改要等服务端处理;Ta 服务(ta serve)没有在运行或包被关掉时,页面上的操作会在 30 秒后报"Ta 服务没有回应"。
以后
- 任务板可以作为另一个包,基于同样的智能体与频道。
为什么不进核心
pi 的核心只管一个会话怎么跑;多会话由 ta serve 提供(openSession 带 appendSystemPrompt 和 group、prompt、getState、getLastAssistantText),包的服务端部分由宿主运行(pi.host)。智能体和频道只是把这些组合起来的一种产品形态,换一种组织方式(任务板、流水线)就是另一个包,所以它和用户自己做的包一样是可选的 pi 包。核心只提供宿主能力:内置包的加载与开关、包的服务端部分、包的服务端数据及其变更通知。
开发
源码在 web-src/,三个入口:index.tsx(页面)、host.ts(服务端)、extension.ts(pi 扩展);team.ts 是服务端的团队,channels.ts 是页面看到的团队,channels-util.ts 是两边共用的纯逻辑。bun run build 先用 tsc 检查并编出 lib/(单测跑 lib/**/*.spec.js),再用 bun build 把三个入口各打成一个没有 import 的文件。React 取自页面(web-src/react.ts 的 bind(pi.React)),图标用 pi.ui.icons。端到端测试(假模型、真的 ta serve)在 apps/ta/test/builtin-packages.spec.ts。