Skip to content

SDK 权限控制

版本要求:本文档针对 CodeBuddy Agent SDK v0.1.0 及以上版本。

本文档介绍如何在 SDK 中实现权限控制,包括权限模式、canUseTool 回调和工具白名单/黑名单。

概述

CodeBuddy Agent SDK 提供多种权限控制机制:

机制说明适用场景
权限模式全局控制权限行为快速设置整体策略
canUseTool 回调运行时动态审批交互式权限确认
工具白名单/黑名单声明式工具过滤静态策略配置

权限模式

通过 permissionMode(TypeScript)或 permission_mode(Python)设置全局权限行为。

可用模式

模式说明
default默认模式,所有工具操作需要确认
acceptEdits自动批准文件编辑,其他操作仍需确认
plan规划模式,仅允许只读工具
bypassPermissions跳过所有权限检查(谨慎使用)

初始配置

typescript
import { query } from '@tencent-ai/agent-sdk';

const q = query({
  prompt: '帮我重构这段代码',
  options: {
    model: 'deepseek-v3.1',
    permissionMode: 'acceptEdits'  // 自动批准编辑
  }
});

for await (const message of q) {
  console.log(message);
}
python
import asyncio
from codebuddy_agent_sdk import query, CodeBuddyAgentOptions

async def main():
    options = CodeBuddyAgentOptions(
        model="deepseek-v3.1",
        permission_mode="acceptEdits"  # 自动批准编辑
    )

    async for message in query(prompt="帮我重构这段代码", options=options):
        print(message)

asyncio.run(main())

动态修改权限模式

使用 Session/Client API 可以在运行时动态修改权限模式:

typescript
import { unstable_v2_createSession } from '@tencent-ai/agent-sdk';

const session = unstable_v2_createSession({
  model: 'deepseek-v3.1'
});

// 发送第一条消息
await session.send('分析这个项目');
for await (const msg of session.stream()) {
  console.log(msg);
}

// 动态切换到 acceptEdits 模式加速开发
// 注意:这是 unstable API
python
from codebuddy_agent_sdk import CodeBuddySDKClient, CodeBuddyAgentOptions

async def main():
    options = CodeBuddyAgentOptions(model="deepseek-v3.1")

    async with CodeBuddySDKClient(options=options) as client:
        await client.query("分析这个项目")
        async for msg in client.receive_response():
            print(msg)

        # 动态切换权限模式
        await client.set_permission_mode("acceptEdits")

        await client.query("现在帮我修改代码")
        async for msg in client.receive_response():
            print(msg)

canUseTool 回调

canUseTool 回调在工具需要权限确认时触发,允许你实现自定义的权限逻辑。

回调签名

typescript
type CanUseTool = (
  toolName: string,
  input: Record<string, unknown>,
  options: CanUseToolOptions
) => Promise<PermissionResult>;

type CanUseToolOptions = {
  signal: AbortSignal;
  toolUseID: string;
  agentID?: string;
  suggestions?: PermissionUpdate[];
  blockedPath?: string;
  decisionReason?: string;
};

type PermissionResult =
  | { behavior: 'allow'; updatedInput: Record<string, unknown> }
  | { behavior: 'deny'; message: string; interrupt?: boolean };
python
CanUseTool = Callable[
    [str, dict[str, Any], CanUseToolOptions],
    Awaitable[PermissionResult],
]

@dataclass
class CanUseToolOptions:
    tool_use_id: str
    signal: Any | None = None
    agent_id: str | None = None
    suggestions: list[dict[str, Any]] | None = None
    blocked_path: str | None = None
    decision_reason: str | None = None

PermissionResult = PermissionResultAllow | PermissionResultDeny

完整示例:交互式审批

typescript
import { query } from '@tencent-ai/agent-sdk';

const q = query({
  prompt: '帮我分析这个代码库',
  options: {
    model: 'deepseek-v3.1',
    canUseTool: async (toolName, input, options) => {
      console.log(`\n🔧 工具请求: ${toolName}`);
      console.log(`   参数:`, JSON.stringify(input, null2));

      // 只读工具自动允许
      const readOnlyTools = ['Read', 'Glob', 'Grep'];
      if (readOnlyTools.includes(toolName)) {
        return { behavior: 'allow', updatedInput: input };
      }

      // 危险命令拒绝
      if (toolName === 'Bash') {
        const command = input.command as string;
        if (command.includes('rm -rf') || command.includes('sudo')) {
          return {
            behavior: 'deny',
            message: '危险命令被拒绝',
            interrupt: true  // 中断整个会话
          };
        }
      }

      // 其他情况:模拟用户确认
      const approved = await promptUser(`允许执行 ${toolName}?`);

      if (approved) {
        return { behavior: 'allow', updatedInput: input };
      } else {
        return { behavior: 'deny', message: '用户拒绝' };
      }
    }
  }
});

for await (const message of q) {
  console.log(message);
}
python
from codebuddy_agent_sdk import (
    query, CodeBuddyAgentOptions,
    CanUseToolOptions, PermissionResultAllow, PermissionResultDeny
)

async def can_use_tool(
    tool_name: str,
    input_data: dict,
    options: CanUseToolOptions
):
    print(f"\n🔧 工具请求: {tool_name}")
    print(f"   参数: {input_data}")

    # 只读工具自动允许
    read_only_tools = ["Read", "Glob", "Grep"]
    if tool_name in read_only_tools:
        return PermissionResultAllow(updated_input=input_data)

    # 危险命令拒绝
    if tool_name == "Bash":
        command = input_data.get("command", "")
        if "rm -rf" in command or "sudo" in command:
            return PermissionResultDeny(
                message="危险命令被拒绝",
                interrupt=True  # 中断整个会话
            )

    # 其他情况:模拟用户确认
    answer = input(f"允许执行 {tool_name}? (y/n): ")

    if answer.lower() == 'y':
        return PermissionResultAllow(updated_input=input_data)
    else:
        return PermissionResultDeny(message="用户拒绝")

async def main():
    options = CodeBuddyAgentOptions(
        model="deepseek-v3.1",
        can_use_tool=can_use_tool
    )

    async for message in query(prompt="帮我分析这个代码库", options=options):
        print(message)

修改工具输入

可以在 canUseTool 中修改工具的输入参数:

typescript
canUseTool: async (toolName, input) => {
  if (toolName === 'Bash') {
    // 在命令前添加安全检查
    return {
      behavior: 'allow',
      updatedInput: {
        ...input,
        command: `set -e; ${input.command}`
      }
    };
  }
  return { behavior: 'allow', updatedInput: input };
}
python
async def can_use_tool(tool_name, input_data, options):
    if tool_name == "Bash":
        # 在命令前添加安全检查
        return PermissionResultAllow(
            updated_input={
                **input_data,
                "command": f"set -e; {input_data.get('command', '')}"
            }
        )
    return PermissionResultAllow(updated_input=input_data)

处理 AskUserQuestion

当 AI 需要向用户提问时,会调用 AskUserQuestion 工具。你需要在 canUseTool 中处理这个工具。

输入结构

typescript
{
  questions: [
    {
      question: "使用哪个数据库?",
      header: "数据库",
      options: [
        { label: "PostgreSQL", description: "关系型数据库" },
        { label: "MongoDB", description: "文档数据库" }
      ],
      multiSelect: false
    }
  ]
}

返回答案

typescript
canUseTool: async (toolName, input) => {
  if (toolName === 'AskUserQuestion') {
    const questions = input.questions as any[];
    const answers: Record<string, string> = {};

    for (const q of questions) {
      console.log(`问题: ${q.question}`);
      for (let i = 0; i < q.options.length; i++) {
        console.log(`  ${i + 1}. ${q.options[i].label}`);
      }

      // 获取用户输入
      const choice = await getUserChoice();
      answers[q.question] = q.options[choice].label;
    }

    return {
      behavior: 'allow',
      updatedInput: { ...input, answers }
    };
  }

  return { behavior: 'allow', updatedInput: input };
}
python
async def can_use_tool(tool_name, input_data, options):
    if tool_name == "AskUserQuestion":
        questions = input_data.get("questions", [])
        answers = {}

        for q in questions:
            print(f"问题: {q['question']}")
            for i, opt in enumerate(q["options"]):
                print(f"  {i + 1}. {opt['label']}")

            # 获取用户输入
            choice = int(input("选择 (1/2/...): ")) - 1
            answers[q["question"]] = q["options"][choice]["label"]

        return PermissionResultAllow(
            updated_input={**input_data, "answers": answers}
        )

    return PermissionResultAllow(updated_input=input_data)

工具白名单/黑名单

使用 allowedToolsdisallowedTools 声明式地控制工具访问。

配置示例

typescript
const q = query({
  prompt: '分析项目结构',
  options: {
    model: 'deepseek-v3.1',
    // 只允许这些工具
    allowedTools: ['Read', 'Glob', 'Grep'],
    // 或者禁止这些工具
    disallowedTools: ['Bash', 'Write', 'Edit']
  }
});
python
options = CodeBuddyAgentOptions(
    model="deepseek-v3.1",
    # 只允许这些工具
    allowed_tools=["Read", "Glob", "Grep"],
    # 或者禁止这些工具
    disallowed_tools=["Bash", "Write", "Edit"]
)

常用工具名称

工具名功能
Read读取文件
Write写入文件
Edit编辑文件
Glob文件模式匹配
Grep内容搜索
Bash执行 Shell 命令
Task子 Agent 任务
WebFetch获取网页内容
WebSearch网络搜索

最佳实践

  1. 默认使用 default 模式:提供最完整的权限控制

  2. 只读任务使用 plan 模式

    typescript
    permissionMode: 'plan'  // 只允许 Read、Glob、Grep
  3. 结合白名单精确控制

    typescript
    allowedTools: ['Read', 'Glob', 'Grep'],
    permissionMode: 'bypassPermissions'  // 允许的工具自动执行
  4. 危险命令使用 interrupt

    typescript
    return {
      behavior: 'deny',
      message: '危险操作',
      interrupt: true  // 立即中断,不让 AI 继续尝试
    };
  5. 生产环境避免 bypassPermissions:该模式会跳过所有权限检查

相关文档