用 goose 构建应用
如果你的应用需要智能体,不一定要用 GDK 组件自己组装。你可以启动预构建的 goose 二进制文件,通过 Agent Client Protocol (ACP) 与它通信。
你的应用负责界面和工作流。goose 提供智能体、模型连接、工具和已保存的对话。ACP 把两者连起来:应用发送提示,接收流式响应、工具活动和权限请求。goose 桌面版也是这样与后端通信的。
下面的示例使用 Node.js 22 或更高版本和 ACP TypeScript SDK,并用 JavaScript 编写,无需构建步骤即可运行。请先安装 goose CLI 并配置模型提供商。工作目录和已配置的工具应与你想给智能体的访问权限相称。
npm install @agentclientprotocol/sdk@1.5.0
把下面三个 JavaScript 代码块按顺序放进名为 app.mjs 的文件。
通过 stdio 连接
把 goose acp 作为子进程启动。它的 stdin 和 stdout 传输 ACP 消息;stderr 用于日志。SDK 把这些管道变成双向连接。
import { spawn } from "node:child_process";
import { Readable, Writable } from "node:stream";
import * as acp from "@agentclientprotocol/sdk";
const goose = spawn("goose", ["acp"], {
stdio: ["pipe", "pipe", "inherit"],
});
const stream = acp.ndJsonStream(
Writable.toWeb(goose.stdin),
Readable.toWeb(goose.stdout),
);
进程生命周期由你的应用管理。用户使用智能体期间保持它运行,结束后关闭连接和进程。
把更新流式传到界面
发送提示之前先注册处理程序。回合运行期间,goose 会发送 session/update 通知,其中带有会话 ID 和更新内容。文本块到达时追加显示,并用工具调用 ID 更新界面中对应的工具活动。
const app = acp
.client({ name: "my-app" })
.onNotification(acp.methods.client.session.update, ({ params }) => {
const update = params.update;
if (
update.sessionUpdate === "agent_message_chunk" &&
update.content.type === "text"
) {
process.stdout.write(update.content.text);
} else if (update.sessionUpdate === "tool_call") {
console.error(`Tool: ${update.title} (${update.toolCallId})`);
} else if (update.sessionUpdate === "tool_call_update") {
console.error(`Tool ${update.toolCallId}: ${update.status}`);
}
})
.onRequest(acp.methods.client.session.requestPermission, () => ({
outcome: { outcome: "cancelled" },
}));
这个示例会取消权限请求,而不是自动批准。在你的应用中,应把提供的选项展示给用户,并返回用户的选择。权限回调不是沙箱:什么时候请求批准由 goose 配置的模式决定,而工具运行时拥有后端对这台机器的访问权限。
创建会话并发送提示
初始化连接,协商协议版本和能力。然后用运行 goose 的机器上的绝对工作目录创建会话。返回的会话 ID 在后续的提示和通知中标识这段对话。
try {
const result = await app.connectWith(stream, async (agent) => {
const initialized = await agent.request(acp.methods.agent.initialize, {
protocolVersion: acp.PROTOCOL_VERSION,
clientCapabilities: {},
});
if (initialized.protocolVersion !== acp.PROTOCOL_VERSION) {
throw new Error("Unsupported ACP protocol version");
}
const session = await agent.request(acp.methods.agent.session.new, {
cwd: process.cwd(),
mcpServers: [],
});
return agent.request(acp.methods.agent.session.prompt, {
sessionId: session.sessionId,
prompt: [{ type: "text", text: "Say hello in one sentence." }],
});
});
console.log(`\nTurn finished: ${result.stopReason}`);
} finally {
goose.kill();
}
在你希望会话工作的目录中运行:
node app.mjs
提示的响应会报告回合为何停止;助手的文本则在该请求未完成时通过通知到达。不要等到提示响应返回才显示输出。一个回合结束前可能包含多次模型调用和工具调用。
connectWith 会在回调结束时关闭连接。这个示例发送一条提示后就结束;交互式应用要让回调保持运行,或使用 SDK 的 connect API 管理更长期的连接。
继续对话
用同一个会话 ID 发送后续提示,可以保留上下文。独立的对话请创建新会话。如果应用支持多段对话,请按会话 ID 分发更新。
要在重新连接后恢复,请保存会话 ID 和工作目录。检查初始化返回的 loadSession 能力,然后使用 session/load。历史会通过同一个通知处理程序回放。停止按钮应为活动回合发送 session/cancel,而不是杀掉后端。
改用服务器
如果子进程不合适,goose serve 通过 HTTP 和 WebSocket 暴露 ACP。应用通过另一种传输方式使用相同的会话和提示概念。
GOOSE_SERVER__SECRET_KEY='replace-with-a-long-random-secret' goose serve
默认端点是 http://127.0.0.1:3284/acp(WebSocket 为 ws://)。用 X-Secret-Key 认证;浏览器 WebSocket 无法设置该请求头时,改用 token 查询参数。不要把后端密钥放进公开的前端代码。浏览器来源可能还需要显式允许。
认证和来源相关的标志见服务器命令选项,部署和 TLS 见远程服务器指南。
示例和参考
- Top Goose 把 GitHub issue 和 pull request 映射为与
goose acp子进程的持久对话。 - Berd 围绕内置的
goose serve后端,通过 WebSocket 构建自定义桌面界面。 - ACP 协议参考定义了初始化、会话、提示、更新、权限和取消。
- goose ACP 参考记录 goose 特有的方法和类型。