Cell - AI Coding Agent Component
Not translated yet
This page is only written in Chinese so far; the original is shown below.
在 Cell 应用里运行 pi coding agent 的会话。pi 负责模型调用、工具(read、bash、edit、write 等)、会话与上下文压缩;本组件把它接进 Cell:
- 会话和模型运行时由 DI 容器管理,应用停止时自动释放会话;
- 配置写在
cell.yml的cell.ai.codingAgent下,选项名与 pi 的createAgentSession()一致; - 工具写成
AgentTool组件(与 @celljs/ai-agent 共用),pi 扩展写成AgentExtension组件,都可以直接用注入的服务; - 按配置启用 pi 的 MCP 客户端、codemode 和 tool_search;
- 事件按 pi JSON/RPC 模式的格式输出,可直接作为 Server-Sent Events 返回给前端。
要求
Node.js >= 22.19 或 Bun(pi 的要求)。pi 是 ES module 包,本组件用动态 import() 加载它,CommonJS 和 ESM 两种构建都可用,只在第一次创建会话时加载。
安装
bun add @celljs/ai-coding-agent配置
cell:
ai:
codingAgent:
cwd: /srv/repo # 会话的工作目录,工具在这里读写;默认进程当前目录
model: anthropic/claude-sonnet-4-5 # <provider>/<model id>;默认取 pi 的设置,再取第一个有凭据的模型
thinkingLevel: medium
tools: [read, grep, find, ls] # 工具白名单,支持 *;默认 read、bash、edit、write 加扩展注册的工具
appendSystemPrompt: Answer in Chinese.
apiKeys: # 只放进内存,不写 pi 的 auth.json
anthropic: "${ env.MY_ANTHROPIC_KEY }"| 配置 | 默认 | 说明 |
|---|---|---|
cwd | 进程当前目录 | 会话工作目录,也是发现项目资源(AGENTS.md、.pi/)的位置 |
agentDir | ~/.pi/agent | pi 的全局目录:auth.json、models.json、settings.json、扩展、技能 |
model / thinkingLevel | pi 的设置 | 模型写成 <provider>/<model id>,找不到时报 AgentModelNotFoundError 并列出可用模型 |
tools / excludeTools | pi 的默认工具 | 白名单 / 黑名单 |
systemPrompt / appendSystemPrompt | — | 替换 / 追加系统提示词 |
discoverResources | true | 像 pi CLI 一样从 cwd 和 agentDir 加载扩展、技能、提示词模板和 AGENTS.md |
persistSessions | false | 同时把会话写成 pi CLI 的 JSONL 文件,pi --resume 能看到 |
allowModelNetwork | false | 创建模型运行时时是否联网刷新模型目录 |
mcp.enabled / mcp.servers | false / — | 启用 pi 的 MCP 客户端:连接 agentDir(以及受信任项目)mcp.json 里的服务器,再加上 servers(格式同 mcpServers) |
codemode | off | on / only:模型写 JavaScript,在沙箱里调用工具 |
toolSearch | false | 启用 tool_search,让模型查找未声明的工具 |
apiKeys | — | 按 provider 设置 API key,空值忽略;cell.ai.pi.apiKeys(@celljs/ai-pi)也用于会话,这里的同名项优先;pi 也会读 ANTHROPIC_API_KEY 等环境变量 |
使用
创建会话并提问
AgentSessionManager.create() 返回 pi 的 AgentSession,之后直接用 pi 的 API(prompt、steer、followUp、abort、subscribe、messages ……):
import { Autowired, Component } from '@celljs/core';
import { AgentSessionManager } from '@celljs/ai-coding-agent';
@Component()
export class ReviewService {
@Autowired(AgentSessionManager)
protected readonly sessionManager: AgentSessionManager;
async review(): Promise<string | undefined> {
const session = await this.sessionManager.create({ tools: [ 'read', 'grep', 'find', 'ls' ] });
try {
await session.prompt('Review the changes in src/ and list the risks.');
return session.getLastAssistantText();
} finally {
this.sessionManager.dispose(session.sessionId);
}
}
}create() 的参数(cwd、model、thinkingLevel、tools、excludeTools、systemPrompt、appendSystemPrompt)覆盖 cell.ai.codingAgent 中的同名配置。会话按 pi 的 sessionId 保存在内存里:get(id)(不存在时抛 AgentSessionNotFoundError)、find(id)、info(id)、list()、dispose(id)。
用 Cell 组件写工具
工具写成 @celljs/ai-agent 的 AgentTool 组件(本包也导出 AgentTool),工具里可以直接用注入的服务;同一个组件既是编码会话的工具,也是 AgentFactory 创建的通用 agent 的工具:
import { AgentTool } from '@celljs/ai-coding-agent';
import { Autowired, Component } from '@celljs/core';
import { TicketService } from './ticket-service';
@Component(AgentTool)
export class CreateTicketTool implements AgentTool {
readonly name = 'create_ticket';
readonly label = 'Create ticket';
readonly description = 'Creates a ticket in the issue tracker.';
readonly parameters = { type: 'object', properties: { title: { type: 'string' } }, required: [ 'title' ] };
@Autowired(TicketService)
protected readonly ticketService: TicketService;
async execute(_toolCallId: string, params: any) {
const ticket = await this.ticketService.create(params.title);
return { content: [ { type: 'text' as const, text: `Created ${ticket.id}` } ], details: {} };
}
}parameters 是 JSON Schema;工具失败时抛异常。配置了 tools 白名单时,工具名要列进去。
用 Cell 组件写 pi 扩展
工具以外的扩展能力(命令、事件处理器、拦截工具调用、注册 MCP 服务器等,见 pi 文档 docs/extensions.md)写成 AgentExtension,register 在每个新会话里拿到 pi 的 ExtensionAPI:
import { AgentExtension, type ExtensionAPI } from '@celljs/ai-coding-agent';
import { Autowired, Component, Logger } from '@celljs/core';
@Component(AgentExtension)
export class AuditExtension implements AgentExtension {
@Autowired(Logger)
protected readonly logger: Logger;
register(pi: ExtensionAPI): void {
pi.on('tool_call', async event => {
this.logger.info(`agent calls ${event.toolName}`);
return event.toolName === 'bash' ? { block: true, reason: 'bash is disabled' } : undefined;
});
}
}register 抛错时创建会话失败,报 AgentExtensionError;从磁盘发现的扩展出错只记警告。
MCP、codemode 与 tool_search
pi CLI 默认加载这三个扩展,SDK 默认不加载;在配置里打开即可。MCP 服务器的工具名是 mcp__<服务器>__<工具>,exposure: direct 时直接声明给模型,默认(codemode)时只能在 codemode 脚本里调用:
cell:
ai:
codingAgent:
codemode: "on"
mcp:
enabled: true
servers:
docs:
url: https://example.com/mcp
headers:
Authorization: "Bearer ${ env.DOCS_TOKEN }"
filesystem:
command: npx
args: [-y, "@modelcontextprotocol/server-filesystem", "."]
exposure: direct配置了 tools 白名单时,要把 codemode、tool_search 或 mcp__* 也列进去。AgentExtension 里也可以用 pi.registerMcpServer(name, config) 按需注册服务器。
以 Server-Sent Events 返回事件
AgentEventUtil.stream(session, response, run) 在 run 执行期间把会话事件写成 SSE,结束后关闭响应;run 失败时写一个 error 事件:
import { AgentEventUtil, AgentSessionManager, type EventStreamResponse } from '@celljs/ai-coding-agent';
import { Autowired } from '@celljs/core';
import { Body, Controller, Param, Post, Res } from '@celljs/mvc/lib/node';
@Controller('sessions')
export class AgentController {
@Autowired(AgentSessionManager)
protected readonly sessionManager: AgentSessionManager;
@Post(':id/prompt')
async prompt(@Param('id') id: string, @Body('text') text: string, @Res() response: EventStreamResponse): Promise<void> {
const session = this.sessionManager.get(id);
await AgentEventUtil.stream(session, response, () => session.prompt(text));
}
}每帧是 event: <type>\ndata: <JSON>,JSON 与 pi --mode json 的事件相同(pi 文档 docs/json.md):message_update 只带增量和 usage,完整消息在 message_end 里,agent_settled 表示这轮结束。另有本组件加的 { "type": "error", "message": "..." }。客户端断开不会中止会话,需要时调用 session.abort()。完整的增删查、提问、中止接口见 examples/coding-agent-app。
配置模型运行时
所有会话共享一个 pi ModelRuntime(ModelRuntimeProvider)。要注册自定义 provider、从密钥服务读取凭据,实现 ModelRuntimeContribution:
import { type ModelRuntime, ModelRuntimeContribution } from '@celljs/ai-coding-agent';
import { Autowired, Component } from '@celljs/core';
import { SecretStore } from './secret-store';
@Component(ModelRuntimeContribution)
export class GatewayModels implements ModelRuntimeContribution {
@Autowired(SecretStore)
protected readonly secrets: SecretStore;
async configure(runtime: ModelRuntime): Promise<void> {
await runtime.setRuntimeApiKey('openai', await this.secrets.get('openai'));
}
}测试
@celljs/ai-coding-agent/lib/node/test 提供基于 pi faux provider 的脚本化模型,不联网、不读写 ~/.pi:
import { AgentSessionManager } from '@celljs/ai-coding-agent';
import { createTestContainer } from '@celljs/ai-coding-agent/lib/node/test';
const { container, faux } = await createTestContainer({ tools: [ 'create_ticket' ] }, myModule);
faux.reply({ tool: 'create_ticket', args: { title: 'Fix login' } }, 'Created.');
const session = await container.get<AgentSessionManager>(AgentSessionManager).create();
await session.prompt('File a ticket for the login bug');createTestContainer 把会话的 cwd 和 agentDir 放在一个新临时目录里,默认模型是 FauxModels.MODEL,并关闭资源发现。
安全
pi 以服务进程的权限执行工具(读写文件、运行命令),没有沙箱。通过 HTTP 暴露会话时要加鉴权(如 @celljs/security),用 tools 只开放需要的工具,并把 cwd 限定在允许 agent 访问的目录;不要让客户端传入 cwd。详见 pi 文档 docs/security.md。
构建产物
pi 在 cell build 时保持为外部依赖(cell.optionalDependencies),连同它的依赖复制到 .cell/output/server/node_modules(约 160 MB),产物目录可以单独部署。