Skip to content

工具与审批

用你自己的运行时动态工具扩展一轮,并用回调回答审批用户输入提问。这三者都是每个 SDK 的一部分。

运行时动态工具

在开启(或恢复)线程时声明工具。工具规格会通过 wire 发送;处理器在你的进程内运行,绝不会被序列化。Agent 调用工具,你的处理器返回结果。

ts
const thread = await dotcraft.threads.start({
  userId: "me",
  dynamicTools: [
    {
      namespace: "myapp",
      name: "GetIssue",
      description: "Read an issue from MyApp.",
      inputSchema: { type: "object", properties: { id: { type: "string" } }, required: ["id"] },
      handler: async (call) => ({
        success: true,
        structuredResult: await getIssue(call.arguments.id as string),
      }),
    },
  ],
});
csharp
var tools = new[]
{
    new DynamicToolSpec("myapp", "GetIssue", "Read an issue from MyApp.", inputSchema),
};

var thread = await client.Threads.StartAsync(
    new DotCraftThreadStartRequest(
        new SessionIdentity("my-app", Environment.UserName),
        DynamicTools: tools));

using var registration = thread.OnToolCall("myapp", "GetIssue", async (call, ct) =>
{
    var id = call.Arguments.GetProperty("id").GetString();
    var issue = await GetIssueAsync(id!, ct);
    return new DynamicToolResult(Success: true, StructuredResult: issue);
});
python
tools = [
    {
        "namespace": "myapp",
        "name": "GetIssue",
        "description": "Read an issue from MyApp.",
        "inputSchema": {"type": "object", "properties": {"id": {"type": "string"}}, "required": ["id"]},
    }
]

thread = await dotcraft.threads.start(user_id="me", dynamic_tools=tools)

thread.on_tool_call("myapp", "GetIssue", lambda call: {
    "success": True,
    "structuredResult": get_issue(call["arguments"]["id"]),
})

处理器返回成功结果(success: true,带 contentItems / structuredResult)或失败(success: false,带 errorCode / errorMessage)。若未注册处理器,SDK 返回 UnsupportedTool;若处理器抛错,返回 AdapterToolCallFailed。工具处理器负责参数校验与应用级授权。

TIP

对于 App Binding 应用,请使用共享的 App Binding 错误形态(appBindingToolError / DotCraftAppBindingClient.ToolError / app_binding_tool_error),而非通用失败。参见 构建应用

审批

当 Agent 为敏感操作请求审批时,SDK 会把它路由到你的处理器,由处理器返回决策(acceptacceptForSessionacceptAlwaysdeclinecancel)。没有处理器时 SDK 会自动接受——生产环境请提供显式处理器。

ts
const dotcraft = await DotCraft.local({
  workspacePath: "/path/to/workspace",
  approvalHandler: async (request) => {
    return confirmWithUser(request) ? "accept" : "decline";
  },
});
csharp
await using var client = await DotCraftClient.ConnectLocalAsync(
    "/path/to/workspace",
    new DotCraftLocalClientOptions
    {
        ClientName = "my-app",
        ApprovalHandler = async (request, ct) =>
            await ConfirmWithUserAsync(request, ct) ? ApprovalDecision.Accept : ApprovalDecision.Decline,
    });
python
dotcraft = await DotCraft.connect_local(LocalOptions(
    workspace_path="/path/to/workspace",
    approval_handler=lambda request: "accept" if confirm_with_user(request) else "decline",
))

用户输入

Plan Mode 和某些工具会向用户提出结构化问题。提供一个用户输入处理器返回答案。没有处理器时,SDK 返回空答案,从而让非交互式客户端不会阻塞。

ts
const dotcraft = await DotCraft.local({
  workspacePath: "/path/to/workspace",
  userInputHandler: async (request) => ({ answers: await askUser(request) }),
});
csharp
var options = new DotCraftLocalClientOptions
{
    ClientName = "my-app",
    UserInputHandler = async (request, ct) =>
        new UserInputResponse(await AskUserAsync(request, ct)),
};
python
dotcraft = await DotCraft.connect_local(LocalOptions(
    workspace_path="/path/to/workspace",
    user_input_handler=lambda request: ask_user(request),  # 返回 answers dict
))

参见

Apache License 2.0