Local Agent

Agent 构造参数

详细说明 Workspace env、WorkspaceTools 以及本地 Agent 的 id、instruction、model、tools 和 plugins 参数

Agent 构造参数

本地 Agent 的构造方式:

const workspace = new Workspace({
  path,
  shell,
  env,
});

new Agent({
  id,
  workspace,
  model,
  instruction,
  tools,
  plugins,
  Session,
})

id

agent 的稳定标识。

它会影响 SDK session 落盘路径:

<project_root>/.downcity/agents/<agent_id>/...

推荐:

  • 稳定
  • 可 URL 编码
  • 不要随便改

因为它直接决定了 session 落盘目录分区。

workspace

当前 Agent 可以访问的项目资源与安全边界。Workspace 统一持有项目根目录、文件与搜索工具,以及可选 Shell:

const workspace = new Workspace({
  path: "/path/to/project",
  shell,
});

关键点:

  • macOS、Linux 和 Windows 使用同一个 Workspace
  • path 会在 Workspace 构造时解析为真实绝对目录
  • 文件和搜索工具固定限制在 Workspace 内
  • Shell 与文件工具使用同一个项目根目录
  • 一个 Workspace 实例只绑定一个 Agent
  • 多个 Agent 可以指向同一目录,但必须分别创建 Workspace
  • agent.dispose() 会同时释放它独占的 Workspace

如果 path 不存在、不是目录或为空,Workspace 会立即拒绝构造。

instruction

调用方传入的本地 SDK Agent 静态基础指令。

new Agent({
  id: "repo-helper",
  workspace: new Workspace({ path: "/path/to/project" }),
  instruction: [
    "你是一个简洁的代码助手。",
    "解释代码时优先给出明确文件引用。",
  ],
});

关键点:

  • instruction 是静态的,应该保持缓存友好
  • SDK 不会对它做动态变量渲染
  • SDK 不读取项目 prompt 配置文件
  • 如果省略 instruction,SDK 会使用包内最小 core instruction 作为 fallback

宿主需要额外基础指令时,应通过 instruction 显式传入。

model

Agent 持有的默认 AgentModel 实例。AgentModel 可以是 AI SDK LanguageModel 或 City CityModel,不需要调用方提前转换。

new Agent({
  id: "repo-helper",
  workspace: new Workspace({ path: "/path/to/project" }),
  model: openai.responses("gpt-5"),
});

关键点:

  • SDK 不负责选择、保存或恢复模型 ID,只持有 AgentModel 实例
  • CityModel 自身实现 LanguageModelV3,Agent 直接调用
  • Session 没有自己的模型时回退使用 Agent 模型
  • 本地 Session 可通过 session.set({ model }) 覆盖 Agent 模型
  • 有效模型解析顺序固定为 Session 模型、Agent 模型

Workspace 的 env

Env 描述当前 Workspace 的项目执行环境,由 Workspace 持有:

const workspace = new Workspace({
  path: "/path/to/project",
  env: {
    OPENAI_API_KEY: process.env.OPENAI_API_KEY ?? "",
  },
});

关键点:

  • SDK 先读取项目 .env,再用 Workspace 构造参数 env 覆盖同名值
  • Env 属于 Workspace,不属于 Agent
  • 如果宿主需要合并多层 env,应先在外部合并,再把最终结果传给 env
  • 这些值会成为 Workspace 的运行时 env,但不会自动回写系统环境变量

运行时更新 env

构造 Workspace 之后,可以在运行期增量更新或整体替换 configured env:

workspace.set_env({ FOO: "1" });
workspace.get_env();                // => { FOO: "1" }
workspace.patch_env({ BAR: "2" }); // 增量合并
workspace.patch_env({ FOO: null }); // null 表示删除
workspace.set_env({ ONLY: "ok" }); // 整体覆盖

关键点:

  • get_env() 返回浅拷贝快照
  • patch_env()null / undefined 表示删除该 key
  • set_env() 整体覆盖当前 Workspace env
  • agent.set_instruction() 只影响之后新建的 Session,不会回溯修改当前内存中的已有 Session
  • Session 恢复时若没有 instruction.md,会从 Agent 当前 instruction 和 plugin 重新生成 system;已有 Session 可用 session.syncshot() 手动刷新,并用 session.snapshot() 持久化
  • Workspace env 与 plugin 修改会在已有 Session 的下一个 Session step 检查点提交
  • 正在进行的 provider 请求及其工具调用继续使用当前 step 已生效的 env
  • 如果当前 Session turn 没有后续 step,排队中的修改会在下一个 Session turn 开始时提交
  • 配置真正生效时,Session timeline 会写入 completed action message
  • 运行时变更只活在内存,不会回写项目 .env 或系统环境变量

tools

Agent 最终持有的额外自定义工具集合。

关键点:

  • Workspace 提供文件、搜索和可选 Shell 组成的 WorkspaceTools
  • PluginRegistry 提供 plugin_readplugin_call
  • AgentOptions.tools 提供调用方自定义工具
  • ask_question 等可选工具需要调用方从 @downcity/agent/tools 导入并显式传入
  • Agent 统一注册这些工具并由 Session 共享
  • 任意来源的工具重名会直接报错,不会静默覆盖

这很适合“多个 session 共用一套默认工具能力”的场景。

Workspace 的 shell

详细配置参见 Shell 与沙箱

可选的内建 shell 能力。

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

const agent = new Agent({
  id: "repo-helper",
  workspace: new Workspace({
    path: "/path/to/project",
    shell: new Shell({ sandbox: new MacOsSeatbeltSandbox() }),
  }),
});

关键点:

  • Shell 不是 plugin
  • Shell 只挂载 shell_execshell_session
  • Workspace 自身挂载 grepfindreadwriteedit,没有 Shell 时仍可使用
  • session 与 turn 上下文由 Agent 内部自动接线
  • 审批归属具体 Session,使用 session.interactions()session.respond(...)session.set({ security })
  • 文件和搜索工具固定限制在 Agent 项目根目录内,不提供 unrestricted 模式

plugins

这里接收的是已经实例化好的 BasePlugin 对象。

例如:

import { Agent, Workspace } from "@downcity/agent";
import { SkillPlugin } from "@downcity/plugins/skill";

const agent = new Agent({
  id: "repo-helper",
  workspace: new Workspace({ path: "/path/to/project" }),
  tools: {},
  plugins: [new SkillPlugin()],
});

联网能力是显式选择的,并且必须提供 provider,请参阅 Web Plugin 文档

SDK 会为当前 Agent 创建独立的 plugin registry。

关键点:

  • 应该传 new SkillPlugin() 这类实例,而不是原始 plugin 定义对象
  • 它不会默认注册全部内建 plugin
  • 它不会复用全局 runtime 的 plugin manager
  • 同名 plugin 被重复注册时会直接报错
  • agent.pluginsPluginContext.plugins 会使用这份 registry
  • 按 Agent 的实际需要实例化对应 plugin,并将这些实例显式传入 plugins

Session

本地 session 的高级自定义类。

当你希望替换本地 session 实现,或者在 Session 层注入自定义 Composer 时使用它。Agent 只负责用这个类创建和恢复 session,不理解 Composer 的具体策略。

import {
  Agent,
  Workspace,
  DefaultSessionComposer,
  Session,
  type SessionOptions,
  type SessionComposeInput,
} from "@downcity/agent";

class CustomSessionComposer extends DefaultSessionComposer {
  override async compose(input: SessionComposeInput) {
    const step = await super.compose(input);
    return {
      ...step,
      system: [
        ...step.system,
        { role: "system" as const, content: "始终简洁回答。" },
      ],
    };
  }
}

class CustomSession extends Session {
  constructor(options: SessionOptions) {
    super({
      ...options,
      composer: new CustomSessionComposer(),
    });
  }
}

const agent = new Agent({
  id: "repo-helper",
  workspace: new Workspace({ path: "/path/to/project" }),
  session_class: CustomSession,
});

关键点:

  • 传入的是 class,不是已经创建好的 session 实例
  • 这样 Agent 可以按不同 session_id 创建多个 session
  • Composer 自定义留在自定义 Session 类内部
  • Composer 只读取 Session 快照并组装 system、history、tools 或压缩计划,不直接持久化消息
  • RemoteAgent 不支持本地 Session 类,因为它只是远程 runtime 的访问器

Session Composer 的可定制范围

统一的 SessionComposer 取代了旧的 System、History、Context 和 Compaction 四类 Composer。自定义 Session 只需要提供一个 Composer:

方法用途
compose(input)返回当前模型 Step 使用的 systemmessagestools
compact(input)根据只读 Message 快照返回 SessionCompactionPlan,无需压缩时返回 null
should_compact(error)判断模型错误是否需要在持久化压缩后重试

compose(input) 可以:

  • 增删或重排 system messages
  • 筛选、转换或注入本轮模型 messages
  • 调整当前 Step 可见的 tools

Composer 不消费 Prompt Queue,不控制 Turn,也不写 Message、Metadata、Mutation 或 JSONL。User Message 会在调用 Composer 前由 Session 持久化,所以自定义 Composer 不需要把当前 query 再追加到 history。

压缩也分为策略和提交两个阶段:Composer 只返回计划,Session 在 Assistant 草稿完成后提交 Segment。这样自定义策略不会绕过 Session 的消息一致性和恢复机制。