新增 Tool 实战手册

本手册面向需要向 Tether 注入新工具的开发者。无论是一个轻量的单个函数工具,还是带有文件交互的重型系统工具,都可以在 5 分钟内快速上线。

快速步骤

  1. 确认工具所依赖的能力接口(例如 IFileSystem 或自定义服务)。
  2. 在插件类上声明 [Inject(...)] 特性并继承 Plugin 基类。
  3. 使用 AIFunctionFactory.Create 构建强类型工具。
  4. 调用 context.Effect(tools.Register(...).Dispose) 把注册随作用域回收。

示例:编写 fetch_url_summary 工具

假设我们需要一个抓取网页文本并返回摘要的工具:

using System.ComponentModel;
using Cordis;
using Microsoft.Extensions.AI;
using Tether.Core;
using Tether.Web;

namespace MyPlugins.Cookbook;

[Inject(typeof(IWebService), typeof(IToolRegistry))]
public sealed class WebSummaryToolPlugin : Plugin
{
    protected override void Apply(Context context)
    {
        var web = context.Require<IWebService>();
        var tools = context.Require<IToolRegistry>();

        var summaryTool = AIFunctionFactory.Create(
            async (
                [Description("完整的 HTTP/HTTPS 地址")] string url,
                [Description("最大提取字符数,默认 2000")] int maxChars = 2000,
                CancellationToken cancellationToken = default
            ) =>
            {
                var result = await web.FetchAsync(url, cancellationToken);
                var content = result.Text;
                if (content.Length > maxChars)
                {
                    content = content[..maxChars] + "\n...(内容已截断)";
                }

                return new
                {
                    finalUrl = result.FinalUrl,
                    kind = result.Kind.ToString(),
                    contentLength = content.Length,
                    text = content
                };
            },
            name: "fetch_url_summary",
            description: "抓取指定 URL 网页的正文内容并提取关键文本");

        // 声明该工具为只读无副作用,允许与其他只读工具并发执行
        var options = new ToolRegistrationOptions
        {
            IsConcurrencySafe = _ => true
        };

        // 注册随插件作用域回收
        context.Effect(tools.Register(summaryTool, scope: null, options).Dispose);
    }
}

最佳实践清单

  • 参数描述必须清晰:每个参数必须带有 [Description(...)] 特性,说明含义与边界条件(如单位是秒还是毫秒、路径是否必须为绝对路径)。
  • 取消令牌传递:异步方法参数中包含 CancellationToken,大模型不会在入参中看到它,但运行时会自动注入当前 turn/step 的取消令牌。
  • 结构化返回值:尽量返回强类型匿名对象或 DTO,而不是拼接的大段不规则字符串。这便于模型进行精准的 JSON 字段抽取。
  • 异常处理由管线兜底:工具方法内抛出的未捕获异常会被 Tether 的工具管线截获,包装为稳定的 TOOL_ERROR 失败码并向模型返回友好错误信息,不会导致 Agent 主循环崩溃。

下一步

在 GitHub 上编辑此页