Agent

Agent SDK

在 Node 应用中嵌入 Downcity Agent 运行时

Agent SDK

@downcity/agent 用于在同一进程中创建 Agent、管理 Session 并执行模型调用。完整 API 参考在 Agent SDK 文档

import { Agent } from "@downcity/agent";
import { Workspace } from "@downcity/workspace";
import { Shell } from "@downcity/workspace";
import { MacOsSeatbeltSandbox } from "@downcity/sandbox-macos";

const workspace = new Workspace({
  id: "project",
  path: process.cwd(),
  shell: new Shell({ sandbox: new MacOsSeatbeltSandbox() }),
  env: { API_KEY: process.env.API_KEY ?? "" },
});

const agent = new Agent({
  id: "repo-helper",
  model,
  tools: { search: search_tool },
  plugins: [plugin],
});

// Session 在创建时选择 Workspace。
const session = await agent.sessions.create({ workspace });
const turn = await session.prompt({ query: "检查当前项目" });
const result = await turn.finished;

await agent.dispose();

Workspace 来自 @downcity/workspace,拥有项目文件、搜索工具、Env、私有存储能力与可选 Shell,但不拥有 Agent 身份、Plugin 或 Session 语义。agent.sessions.create({ workspace }) 在当前 Agent 的私有存储作用域内创建 SessionStore。

Agent 配置不绑定 Workspace。同一个 Agent 可以进入多个 Workspace;多个 Agent 也可以通过各自的 Workspace 实例操作同一物理目录。agent.dispose() 会离开所有已进入的 Workspace,并释放其中的 Shell 资源。

本地运行状态集中保存在项目目录之外:

~/.downcity/agents/<agent_id>/workspaces/<workspace_id>/

Agent 在构造时启动 plugin 生命周期与后台任务,Session 执行会自动等待初始化完成;关闭时使用 await agent.dispose()。HTTP 与 RPC 由宿主 City 通过 city.http()city.rpc() 提供。

流式消息

subscribe() 返回统一的实时 Session Mutation:

const unsubscribe = session.subscribe((mutation) => {
  if (mutation.variant === "delta" && mutation.type === "text") {
    process.stdout.write(mutation.delta);
  }
});

Tool 生命周期使用 type: "tool",用户参与使用 type: "interaction"。pending Interaction 携带完整请求,关联 Tool 同时进入 waiting-user;响应通过独立 Session 命令提交:

同一 Assistant step 内的 Part 顺序始终与模型流一致。即使 Tool 已提前进入执行准备,它也会等待对应的 Tool Part 出现在流中,不会越过尚未写入的前置文本。等待按 tool_call_id 相互隔离,多个 Tool 仍可并发执行;客户端应按 Mutation 与历史快照中的顺序直接渲染,无需自行重排。

const unsubscribe = session.subscribe((mutation) => {
  if (
    mutation.variant === "part" &&
    mutation.type === "interaction" &&
    mutation.part.status === "pending"
  ) {
    render_interaction(mutation.part);
  }
});

async function decide_approval(interaction_id: string, decision: "approved" | "denied") {
  await session.respond({
    interaction_id,
    response: { type: "approval", outcome: decision === "approved" ? "resolved" : "denied", payload: { decision } },
  });
}

历史快照使用 session.messages()。它返回当前 Active 页;存在更早的不可变 Segment 时,会同时返回 next_before_sequence 游标。连接断开后重新读取快照并建立新的订阅。

需要渲染平铺活动时间线的宿主可以直接使用 SDK 投影,不需要依赖 .downcity 的物理文件名:

import { to_session_message_timeline_events } from "@downcity/agent";

const page = await session.messages();
const timeline = page.items.flatMap(to_session_message_timeline_events);

Session JSONL、日志和调度任务通过 AgentWorkspace 私有 FileSystem 完成根目录限制、原子写入与跨进程文件事务。项目 File/Search 工具无法访问这些私有状态;应用应通过 AgentWorkspace 与 Session API 访问结构化状态。

继续阅读: