← 返回文章列表

Dify 插件开发实验(02):参数与凭证体系——插件参数和凭证如何声明、配置与管理?

1. 业务场景

先讲一个我们实际遇到的场景。

客服工单 SaaS 的客服每天要接几十通电话,用户开口第一句往往是「我的订单怎么还没处理」。客服要做的第一件事,就是打开工单系统,按用户报的工单号查状态——是正在处理、已完成,还是根本没查到。这个「按工单号查状态」的动作,背后是一次带密钥的系统调用:客服侧应用要拿一个 api_key 去访问工单系统,才能换回订单状态。

我们第一次接这类需求时,第一反应也是「调个接口而已,密钥写死在代码里不就行了」。真正动手才发现——密钥怎么放、参数怎么校验、错误怎么分层,每一件都比调通接口更磨人:密钥散落在代码和日志里,泄露一次就是安全事故;工单号格式错了没人知道错在哪;查询失败只有一句「查询失败」,客服和运维都只能干瞪眼。

这不是个例。任何「一个系统要调用另一个系统」的业务场景都是这个模式:CRM 查客户、财务查发票、物流查轨迹——调用方要传参数,还要带凭证,参数错了、凭证错了、业务不存在了,返回的错误各不相同。

2. 场景痛点

这个流程的痛点,在客服团队身上体现得最直接:

本质上,参数与凭证是工具交付给下游的「接口契约」——契约不清晰、密钥不安全,工具就只是「能跑」,远谈不上「能交付」。

3. 方案:为什么是插件凭证体系

选插件凭证体系,我们实际对比过:

这篇文章我们就用它搭一个「工单查询」工具插件:必填参数 order_id(格式校验 WO- 开头 + 8 位数字)+ provider 级凭证 api_key,跑通「声明 → 配置 → 注入」的完整凭证链路,并用本地 mock 工单服务验证参数/凭证/业务三层错误的分层返回。

4. 整体架构

graph TD start["开始(order_id 输入)"] tool["工具节点 get_order_status(凭证注入 api_key)"] check{"IF-ELSE(输出文本包含 'error'?)"} err["错误输出 end_error"] ok["正常输出 end_ok"] start --> tool --> check check -- "是" --> err check -- "否" --> ok

IF-ELSE 分流用 contains / not contains "error" 判断(工具统一返回 {error:{code,message}} 结构)。

链路很清晰:入口收工单号 → 工具校验参数 → 注入凭证调工单系统 → 按 error 结构分流。关键设计是凭证与参数分离——参数随调用走,凭证在 provider 层配置,密钥永远不经过调用方。

5. 模块设计

5.1 凭证声明(provider/order_tool.yaml)

credentials_for_provider:

  api_key:

    type: secret-input          # 密钥型:加密存储、UI 脱敏

    required: true

    label:

      en_US: API Key

    placeholder:

      en_US: Please input your API Key

  base_url:

    type: text-input            # 普通文本:服务地址

    default: http://127.0.0.1:8002

    required: false

tools:

  - tools/get_order_status.yaml

5.2 参数声明(tools/get_order_status.yaml)

parameters:

  - name: order_id

    type: string

    required: true

    form: llm

    llm_description: 'Order id, must match format WO- followed by 8 digits, e.g. WO-20260805'

注意:schema 层没有 pattern 字段,格式校验在工具代码内做(ORDER_ID_PATTERN = re.compile(r"^WO-\d{8}$"))。

5.3 凭证三要素 API 流程(实测)

声明 → 配置 → 注入,三步缺一不可:

  1. 声明:provider yaml 的 credentials_for_provider(secret-input / text-input)。
  2. 配置:控制台插件凭证页录入(POST /tool-provider/builtin/order_tool/add 创建凭证记录)。
  3. 必须设为默认:POST /tool-provider/builtin/order_tool/default-credential(body {id: credential_id})——实测 add 之后 is_default=false,运行时不注入;set 之后才注入。

5.4 错误分层(tools/get_order_status.py)

if not order_id:

    yield ... {"error": {"code": "param_invalid", "message": "order_id is required"}}

if not ORDER_ID_PATTERN.match(order_id):

    yield ... {"error": {"code": "param_invalid", "message": "order_id must match WO-XXXXXXXX"}}

api_key = self.runtime.credentials.get("api_key", "")

if not api_key:

    yield ... {"error": {"code": "auth_failed", "message": "api_key is not configured"}}

# 调工单系统:401 → auth_failed;404 → not_found;非 200 → upstream_error

# RequestException 异常 → upstream_error(含超时)

四 code 统一:param_invalid(调用前可拦截)/ auth_failed(认证失败)/ not_found(业务不存在)/ upstream_error(上游故障),不抛裸异常。

6. 运行验证

输入 预期 结果
WO-20260805(合法工单) 返回 processing 通过
WO-20260804(合法工单) 返回 done 通过
缺 order_id / 非法格式 abc param_invalid 通过
未配置凭证 / 错误凭证 auth_failed 提示去配置 通过
WO-99999999(不存在) not_found(区分于参数错) 通过
workflow 集成 IF-ELSE 按 error 分流(end_ok/end_error 正确) 通过
日志检查 无 api_key 明文 通过(DB 密文 HYBRID 前缀 + UI 脱敏 mo******06)

7. 实战坑

现象 修复
provider_type 写 plugin DSL 校验能过但运行时凭证永远空,报 auth_failed「api_key is not configured」 必须写 builtin——ToolManager match 只认 BUILT_IN case,凭证注入在该分支内 isinstance 处理(实测,重大修正)
凭证配置后未注入 add 创建凭证记录后 is_default=false(DB 实测),运行时不注入 必须 POST default-credential 设为默认(实测)
插件访问宿主机不通 127.0.0.1 指向 daemon 容器自己 → ConnectionError upstream_error 宿主机服务用 http://host.docker.internal:端口(实测)
schema 无 pattern 字段 参数格式校验无处声明 工具代码内 ORDER_ID_PATTERN re.match 校验(实测)
错误结构不统一 下游分支难以处理 统一 {error:{code,message}} 四 code,IF-ELSE contains '"error"' 分流(实测)
凭证泄漏进日志 api_key 明文暴露风险 加密存储(DB 密文)+ UI 脱敏,工具错误信息不含密钥(实测)

8. 实验文档及源码获取

文章聚焦核心配置与采坑点;实验的完整分步操作(节点搭建/参数表/调试指引)见实验文档原文。

联系我

15088711270

手机端点击号码可直接拨打 · 桌面端可复制

微信二维码

扫码加微信 · 备注「门户」更快通过