实验 03 — 工具¶
目标: 使用以下项目,以模型可调用的真实 C# 函数替换静态提示内容: CopilotTool.DefineTool 以及 SessionConfig.Tools.
时间: 约 20 分钟
先决条件: 实验 02 已完成。
步骤 1 — 为何需要工具¶
在实验 02 中,您将零售数据放入系统消息,让助手的回答有所依据。这适用于小型示例,但有三个问题:
- 内容是静态的 — 它只知道您最初粘贴的信息
- 模型必须猜测哪些事实重要
- 每项事实都会消耗令牌,即使回答根本不需要该事实
工具会改变问题的处理方式。您不再需要指望提示中包含正确数据,而是注册一个 C# 函数。模型会判断何时需要该函数、要求 SDK 调用、接收结果,再撰写最终答案。
步骤 2 — 查看工具结构¶
打开
ToolsSample.cs
并找到 GetCustomerTotal.
Copilot SDK 工具一开始只是一个普通的 C# 方法:
[Description("Gets the total amount a given retail customer has spent.")]
private static string GetCustomerTotal(
[Description("Customer identifier, for example C003")] string customerId)
{
...
}
最重要的部分是 [Description] 元数据,来源为
System.ComponentModel.
这些描述就是模型的 API 文件。如果方法描述模糊,模型可能找不到工具;如果参数描述模糊,模型可能传入错误值。请像为其他开发人员记录公用 API 一样编写描述。
步骤 3 — 注册工具¶
CopilotTool.DefineTool(GetCustomerTotal) 会将方法转换为
AIFunction。接着,将该函数赋给会话配置:
var modelId = await ModelPicker.PickAsync(client, requestedModelId);
var totalTool = CopilotTool.DefineTool(GetCustomerTotal);
var config = new SessionConfig
{
Model = modelId,
Streaming = false,
Tools = [totalTool]
};
Tools = [totalTool] 正是“模型只能生成文本”与“模型可以要求主机应用程序执行实际工作”之间的关键区别。
ModelPicker 会优先选择 claude-haiku-4.5;如果您的账户无法使用该模型,则会回退到其他可用模型。您也可以通过以下参数覆盖选择:
--model <id>.
步骤 4 — 执行¶
预期输出:
== Lab 03: tools ==
Model: claude-haiku-4.5
Prompt: How much has customer C003 spent in total?
Assistant:
[tool] GetCustomerTotal(C003) -> $1,700.00
Assistant: Customer C003 has spent a total of **$1,700.00** across 2 transactions.
⚠️ 请注意第一个空白 Assistant: 行。这是真实行为:助手消息事件会在工具调用附近触发,早于最终自然语言答案的组合。
步骤 5 — 跟踪实际发生的情况¶
执行作业包含四个操作部分:
- 提示要求查询客户的消费总额:
C003 - 模型判断已注册工具是回答问题的正确方式
- SDK 会调用 C# 方法,生成
[tool]行 - 工具结果会被送回模型,再由模型撰写最终答案
[tool] GetCustomerTotal(C003) -> $1,700.00 这一行并非模拟输出;它来自真正的 GetCustomerTotal 方法,表明 SDK 正在处理模型发起的工具调用。
步骤 6 — 进行实验¶
尝试不存在的客户,例如 C999。工具会返回普通字符串而不是抛出异常,以处理该情况:
重新执行示例,确认助手报告找不到任何交易。
然后试试不需要零售数据的提示词:
模型应该直接回答。由于不需要查询客户,
[tool] 行不应出现。
步骤 7 — 使用权限控制工具执行¶
SDK 还公开了进程内权限钩子:
OnPermissionRequest = (request, invocation) =>
{
return Task.FromResult(PermissionDecision.ApproveOnce());
}
签名如下:
决策来自 GitHub.Copilot.Rpc.PermissionDecision,包括
ApproveOnce() 以及 Reject(string feedback)。另外也有内置快捷键: PermissionHandler.ApproveAll,适用于允许所有请求的示例。
⚠️ 在 GitHub Copilot SDK v1.0.9 中,此权限决策 API 标示为实验性。使用 GitHub.Copilot.Rpc.PermissionDecision 会引发构建错误
GHCP001 ,除非该内容遭到隐藏。示例项目特意在此位置进行此处理:
SdkLabs.csproj:
⚠️ 将此钩子用作强制执行点时,务必格外谨慎。根据我们的测试,该处理程序从未被调用:即使处理程序拒绝每个请求,命令仍会执行,因为主机 CLI 已预先授予工具权限。只有当主机把决策交给 SDK 时,OnPermissionRequest 才会介入。请务必在您的环境中验证其行为,再将它作为控制措施。
请参阅
PermissionsSample.cs
获取参考代码。如需 Shell 钩子治理替代方案,请参阅
扩展实验:治理钩子。
CopilotToolOptions.SkipPermission 也可以在适合您的主机时,让工具不参与权限流程。
✅ 检查点¶
您现在可以说明:
- [x] 了解为何工具优于将动态数据塞入系统消息
- [x] 如何
[Description]属性会引导工具选择与参数设置 - [x] 如何
CopilotTool.DefineTool会将 C# 方法注册为AIFunction - [x] 如何
SessionConfig.Tools会让模型可以使用该函数 - [x] 了解为何权限钩子必须在实际执行的主机中验证
💡 加分练习¶
添加第二个工具,用于返回客户购买过的商品类别,例如 Electronics 以及 Fashion 用于 C003。请为方法及其参数提供精确的 [Description] 属性,并将它注册在以下项目旁:
GetCustomerTotal,随后询问:
确认模型调用一个工具、两个工具,或直接回答:
相关内容¶
- 上一步: 实验 02 — 您的第一次流式传输对话
- 下一步: 实验 04 — 事件
- 演示:Copilot SDK 整合
- 故障排除