🐦 第3章 扣子插件和卡片
📄 一句话概括

插件不是一段代码,是一份接口描述。 它告诉模型「有这么个能力,名字叫这个,要填这些参数」, 告诉系统「请求打到哪、返回什么样」。 拆开是四样:名字与描述、地址与方法、参数、返回结构。缺一样就废。 这一节把四样摆开,也讲清「描述」为什么比「地址」更要紧。

📝 本章和前面几章的关系

第 2 章把平台拆成五层,这一章讲能力层里的一半:插件。另一半是卡片,3.4 节讲。 插件这个概念在 21 课(Web 技术原理)和 37 课(图解 HTTP)里都出现过, 那两本讲的是「HTTP 请求怎么发出去」,这一节讲的是「怎么把它包装成模型能用的东西」。 读完这一节,第 8 到 16 章那些案例里凡是出现「插件节点」的地方,你都知道它在干什么。

一、插件是一张说明书

很多人第一次接触插件,以为要写代码。其实平台上的「插件」大部分时候是填表:填名字、填地址、填参数。 填完这些东西,平台就多了一个能调用的能力。 真正干活的代码在别人家服务器上,你只是告诉平台「怎么去敲那扇门」。 所以准确的说法是:插件是一张说明书,不是一台机器。机器在别人那儿,说明书告诉你机器在哪、 要投什么币、出来什么东西。

这也解释了为什么平台上的插件那么多:写说明书比造机器便宜太多。

二、四样,缺一样就废

名字与描述给模型看。它靠这段决定「这个工具能不能办用户这件事」。名字要短,描述要说人话。 地址与方法给系统用。请求打到哪个 URL、用 GET 还是 POST。 这一项写错就是 404,写不全平台直接不让存。 参数给调用方填。每个参数要有名字、类型、必填与否。 类型写错(比如把 string 写成 text)平台会拦。

返回结构给下游解析。 这一项最容易忽略,因为平台不一定强制你填——但下游节点要用里面的字段,字段名对不上就白调。 演示里五份描述只有一份能存进去。剩下四份各缺一样,报的错都很直白。

三、描述比地址更要紧

地址写错,调不通,你马上知道。描述写错,调得通,但永远不会被选中——你甚至不知道它没被用过。 演示里四份描述对三句请求打分。「查股票今天的股价」在「茅台今天股价多少」那一列得分最高; 「金融数据查询接口 v2」四列全零。 最后那份技术上完全正确,参数、地址、返回结构都没问题。 它只是用了接口的语言,而不是用户的说法。

所以写描述的规矩只有一条: 用用户会说的话,写这个工具能办的事。「查询金融数据」不如「查股票今天的股价」。

四、平台里的三类插件

官方插件:平台自己维护的,搜索、网页读取、图片生成这些。稳定,但能力有限。 第三方插件:别人发布到插件市场的。选的时候看两件事——最近更新时间和调用量。 半年没更新的,接口多半已经变了。 自定义插件:自己写的。3.3 节整节讲这个。

三类里官方的最省事,第三方的最容易踩坑,自定义的最灵活。能解决同一个问题的时候,按这个顺序挑。

五、插件的边界:读还是写

插件分两种,这个区分比什么都重要。 只读的:查天气、查股价、搜网页、读文档。调错了最多给个错答案,世界没有变化。 有副作用的:发消息、下单、改数据、删文件。调错了世界变了,而且常常变不回来。

对只读插件,可以让模型自己决定什么时候调。 对有副作用的,必须走工作流加确认——这一条第 3.2 节还会讲一次。 判断方法很简单:问一句「调错了会怎样」。 答「答案不对」是只读,答「发出去了」是副作用。

六、动手 Demo:五份描述,四份存不进去

造五份插件描述:一份完整的,四份各缺一样。逐个过一遍平台的保存检查。 再拿四份描述对三句真话打分,看哪份会被选中。

def spec(name, desc, url, params):
    return {'name': name, 'description': desc,
            'url': url, 'parameters': params}

S = spec('查股价', '查股票今天的股价',
         'http://x/stock', [('code', 'string', True, '六位代码')])

def check_spec(s):
    bad = []
    if not s['name']:        bad.append('缺 name')
    if not s['description']: bad.append('缺 description')
    if not s['url'].startswith(('http://', 'https://')):
        bad.append('url 不是 http 开头')
    for nm, tp, req, _d in s['parameters']:
        if tp not in ('string', 'integer', 'number', 'boolean'):
            bad.append('参数 %s 类型不认识' % nm)
    return bad

实跑输出:

--------------------------------------------------------------------
插件拆开是四样东西
--------------------------------------------------------------------


  ① 四样,缺一样就废
  --------------------------------------------------------------------
  名字与描述:给模型看的,它靠这段决定要不要用。
  地址与方法:给系统用的,请求打到哪、用什么动词。
  参数:给调用方填的,每个要名字、类型、必填。
  返回结构:给下游解析的,字段名对不上就白调。

| 描述 | 校验 | 报了什么 |
|---|---|---|
| 完整 | 通过 | — |
| 缺描述 | 拦下 | 缺 description |
| 地址不完整 | 拦下 | url 不是 http 开头 |
| 参数类型写错 | 拦下 | 参数 code 类型不认识 |
| 必填写成 maybe | 拦下 | 参数 code 必填没填 |

  五份里只有一份能存进去。平台保存插件时做的也是这几个检查。
  ! 注意第三个:地址写 /stock,看着没毛病,但它不是 http 开头——平台会拦,因为拼不出完整请求。

  ② 描述是给模型看的,写法决定它被不被选中
  --------------------------------------------------------------------

| description | 茅台今天股价多少 | 明天宜昌下雨吗 | 把这份报告发给张总 |
|---|:---:|:---:|:---:|
| 查股票今天的股价 | 0.29 | 0.00 | 0.00 |
| 查明天天气与是否下雨 | 0.13 | 0.27 | 0.00 |
| 发给某人一个文件 | 0.00 | 0.00 | 0.12 |
| 金融数据查询接口 v2 | 0.00 | 0.00 | 0.00 |

  每一行都有一个明显偏高的格子,正好落在它该管的那句上。零星的 0.1 是哈希碰撞带来的噪声。
  最后一行叫「金融数据查询接口 v2」——技术上完全正确,但用户不会说这句话,所以四个分数全零。
  ! 描述要写「用户会怎么说这件事」,不是「这个接口在技术上是什么」。

①四样东西那段是本节最该背的。 名字描述给模型,地址方法给系统,参数给调用方,返回结构给下游——四个不同的读者。 ②五份里四份被拦下,报的错都很直白:缺描述、地址不完整、类型不认识、必填没填。 平台保存插件时做的也是这几个检查。 ③注意「地址不完整」那份:写 /stock 看着没毛病,但它不是 http 开头,平台拼不出完整请求,只能拦。 ④第二张表是关键。四份描述四行,三句请求三列。每一行都有一个明显偏高的格子——除了最后一行,全零。 ⑤最后那行「金融数据查询接口 v2」技术上没错,只是没人会这么说话。 描述写的是用户的说法,不是接口的学名。

30 秒实验

  1. 打开平台的插件列表,随便点开三个,看它们的 description 用的是什么词。你会发现写得好的都很口语。
  2. 把你自己的插件描述念给一个不懂技术的人听。他听不懂,说明模型也听不懂。
  3. 给你常用的插件标一下:只读还是副作用。副作用那些,别让模型自己决定调不调。

常见误区

  1. 「插件要会写代码才能做。」大部分是填表。要写代码的是接口本身,那是别人的事。
  2. 「描述随便写,反正模型很聪明。」模型只读到你写的那几个字。 写「金融数据接口」,它就不知道这能查股价。
  3. 「能调通就说明配对了。」调通只说明地址和鉴权对了。 描述写得没人会选,或者返回字段解析不出来,都是「调通但不能用」。
自测三问(点开对答案) 1. 插件的四样东西分别给谁看? —— 名字与描述给模型,地址与方法给系统,参数给调用方,返回结构给下游节点。 2. 为什么说描述比地址更要紧? —— 地址错马上报错,描述错则永远选不中——它调得通,只是从来没人用,你还不一定发现。 3. 怎么判断一个插件该不该让模型自己决定调用? —— 问「调错了会怎样」。答案不对是只读,可以让模型定;发出去了是副作用,必须走工作流加确认。

说明书有了,接下来是真用起来:插件有两种接法,挂给智能体和插进工作流,两种的差别不在于谁更先进, 在于「谁来承担调错的责任」。下一节把这条讲透。

相关笔记