# TOOLKIT WORKFLOW — YAML 驱动的 HTTP 工具链引擎 ## 概述 TOOLKIT WORKFLOW 让你通过编写 YAML 文件来定义外部 HTTP 能力。一个 YAML 文件描述一个能力:它接受哪些参数、调用哪些外部 HTTP API、如何从响应中提取数据、以及最终返回什么给 LLM。 模型初始只暴露三个入口工具:`query_capabilities`、`query_capability_detail` 和 `call_capability`。YAML 能力不会直接出现在初始 tools 数组中,必须先查询能力列表或详情,再通过 `call_capability` 按能力名称和参数调用。 核心能力: - 一个 YAML 文件 = 一个完整的 HTTP 能力,无需写代码 - 支持多步 HTTP 请求链,步骤之间可以传递变量 - 流程控制:`next` 跳转步骤、`jump` 降级到备源、`abort` 终止执行 - 键级缓存:`cache` 缓存变量,同一调用中缺键自动重请求 - 统一提取:`extract` 字段,语法自描述引擎(路径 / 正则) - 工具分类:`api` 能力用于发现和调用 iAs 内部 HTTP API;`http` 能力来自 `tools/*.yaml` 外部 API 配置。 - 内部 HTTP API 不再为每个 endpoint 维护一个重复 AI 能力。模型应先调用 `query_api_docs` 查询功能分组、endpoint_id、参数和请求体,再调用 `call_http_api` 执行具体 endpoint。 --- ## YAML 顶层结构 一个工具 YAML 文件包含以下顶层字段: | 字段 | 必填 | 类型 | 说明 | |------|------|------|------| | `name` | 是 | string | 能力名,对应 `call_capability.name` | | `desc` | 是 | string | 能力描述,会出现在 `query_capabilities` 和能力详情中 | | `params` | 否 | array | 能力参数定义,映射为 `query_capability_detail` 返回的 JSON Schema | | `env` | 否 | array[string] | 该工具依赖的环境变量名列表,仅作文档和校验 | | `steps` | 是 | array | 请求步骤链,按顺序执行 | --- ## `params` — 参数定义 定义 LLM 可以传入的参数,映射为 function calling 的 parameters schema。 | 字段 | 必填 | 类型 | 说明 | |------|------|------|------| | `name` | 是 | string | 参数名,在模板中通过 `${param:key}` 引用 | | `type` | 是 | string | 取值:`string` / `number` / `integer` / `boolean` | | `required` | 是 | boolean | 是否必填 | | `description` | 是 | string | 参数说明,LLM 据此理解参数含义 | | `default` | 否 | any | 默认值,LLM 未传时自动使用 | --- ## `steps` — 请求步骤 每个步骤定义一个 HTTP 请求及其后续处理。 ### 步骤字段 | 字段 | 必填 | 类型 | 默认值 | 说明 | |------|------|------|--------|------| | `url` | 是 | string | — | 请求 URL,支持模板语法 | | `method` | 否 | string | `GET` | HTTP 方法:`GET` / `POST` / `PUT` / `DELETE` / `PATCH` | | `headers` | 否 | map | — | 请求头键值对,值支持模板语法 | | `query` | 否 | array | — | URL 查询参数,格式为 `[{key, value}]` 元组列表,值支持模板语法 | | `body` | 否 | string | — | 请求体,支持模板语法 | | `timeout` | 否 | integer | 30 | 超时时间(秒) | | `cache` | 否 | boolean | false | 是否对该步骤启用键级缓存 | | `next` | 否 | integer | — | 步骤成功后跳转到第几个步骤(0-based),不设则顺序执行下一步 | | `var` | 否 | array | — | 变量提取列表 | | `result` | 否 | string | — | 最终返回模板,有此字段则该步为终步 | ### 步骤示例 ```yaml steps: - url: ${env:BASE_URL}/auth/token method: POST headers: Content-Type: application/json body: | {"client_id": "${env:CLIENT_ID}", "client_secret": "${env:CLIENT_SECRET}"} timeout: 15 cache: true var: - name: token extract: access_token abort: true next: 1 - url: ${env:BASE_URL}/orders/${param:order_id} method: GET headers: Authorization: Bearer ${step0:token} var: - name: status extract: data.status - name: amount extract: data.amount result: | 订单号: ${param:order_id} 状态: ${step1:status} 金额: ${step1:amount} ``` --- ## `var` — 变量提取 每个步骤执行完 HTTP 请求后,按顺序处理 `var` 列表中的条目,从响应中提取命名变量。提取的变量通过 `${stepN:var_name}` 在后续步骤中引用。 ### `extract` — 提取表达式 一个字段覆盖所有提取场景。值的语法决定使用的引擎: | 语法 | 引擎 | 说明 | |------|------|------| | 不以 `/` 开头 | 路径引擎 | 对 JSON/YAML 响应执行结构化路径导航 | | 以 `/` 开头且以 `/` 结尾 | 正则引擎 | 对原始响应文本执行正则匹配,取捕获组 1 | **路径引擎**:自动检测响应格式(先 JSON,失败则 YAML),然后按路径导航取值。 | 语法 | 含义 | 示例 | |------|------|------| | `key` | 访问对象字段 | `data.status` | | `key[N]` | 访问数组第 N 个元素 | `items[0].name` | | `key[]` | 遍历数组所有元素 | `books[]` | | `key[].field` | 遍历数组,对每个元素取字段 | `books[].title` | > 键名建议仅使用 `[a-zA-Z0-9_]`,避免含 `.` `[` `]` 的特殊键名。 **正则引擎**:`/pattern/` 形式,对原始响应文本执行正则匹配,取第一个捕获组。 ```yaml # 路径:从 JSON 响应中提取 var: - name: token extract: auth.access_token # 正则:从任意文本中提取 var: - name: session extract: /session[= ]([a-f0-9]+)/ ``` ### 提取失败处理(每个条目最多选一种) | 配置 | 提取成功 | 提取失败 | |------|---------|---------| | 不设任何失败处理 | 存入变量 | 存入空字符串 `""`,继续执行 | | `default: "值"` | 存入变量 | 存入默认值,继续执行 | | `abort: true` | 存入变量 | 终止整个工具,返回错误给 LLM | | `jump: N` | 存入变量 | 跳转到第 N 个步骤执行,N 从 0 开始 | ### var 条目完整字段 | 字段 | 必填 | 类型 | 默认值 | 说明 | |------|------|------|--------|------| | `name` | 是 | string | — | 变量名,通过 `${stepN:name}` 引用 | | `extract` | 否 | string | — | 提取表达式,语法自描述引擎(路径 / 正则) | | `default` | 否 | string | — | 提取失败时的默认值 | | `abort` | 否 | boolean | false | 提取失败时是否终止工具 | | `jump` | 否 | integer | — | 提取失败时跳转到第几个步骤(0-based) | --- ## 流程控制 三种流程控制机制,覆盖顺序执行、条件跳转和异常终止。 ### `next` — 成功后跳转 - 位置:步骤级别 - 触发条件:步骤所有 var 都没有因 abort/jump 而失败 - 效果:跳转到指定步骤号(0-based),不设则顺序执行下一步 - 用途:跳过某些步骤、实现条件分支 ### `jump` — 提取失败时降级 - 位置:var 条目级别 - 触发条件:该条目的提取失败且未设置 default - 效果:跳转到指定步骤号(0-based),当前步骤剩余 var 不再执行 - 用途:主数据源失败时自动切换到备数据源 ### `abort` — 提取失败时终止 - 位置:var 条目级别 - 触发条件:该条目的提取失败 - 效果:终止整个工具执行,返回错误信息给 LLM - 用途:关键变量(如认证 token)获取失败时立即报错 --- ## `cache` — 键级缓存 当步骤设置 `cache: true` 时,该步骤提取的所有变量会被缓存。 缓存行为: - **命中**:缓存中存在该步骤所有 var.name → 跳过 HTTP 请求,直接返回缓存值 - **未命中**:任意一个 var.name 缺失 → 发起 HTTP 请求,提取所有变量,全部存入缓存 - **生命周期**:单次工具调用期间(内存级),工具返回后释放 - **缓存键**:由步骤的 `url + method + headers + body` 模板展开后的哈希确定 典型用法:第一步获取认证 token 并缓存,后续多步复用 token 时不会重复请求认证接口。 --- ## 模板语法 以下字段支持模板语法:`url`、`headers` 的值、`body`、`result`。 ### 三种引用 | 语法 | 来源 | 示例 | |------|------|------| | `${param:key}` | `call_capability.parameters` 传入的能力参数 | `${param:city}`、`${param:order_id}` | | `${env:VAR}` | 系统环境变量 | `${env:API_KEY}`、`${env:BASE_URL}` | | `${stepN:var_name}` | 第 N 个步骤提取的变量,N 从 0 开始 | `${step0:token}`、`${step1:status}` | ### 约束 1. 禁止嵌套引用:`${step0:${env:KEY}}` 不允许 2. 禁止原始步骤引用:`${step:0}` 不允许,必须先通过 var 提取为命名变量 --- ## `result` — 最终返回 步骤的 `result` 字段定义该工具最终返回给 LLM 的内容。 - 如果某步设置了 `result`,该步即为**终步**,执行后不再继续后续步骤 - 如果所有步骤都未设置 `result`,默认返回最后一步的原始响应体 - `result` 支持模板语法,可以拼接多个变量和文字 ```yaml # 返回单个变量 result: ${step1:status} # 多行模板拼接 result: | 订单号: ${param:order_id} 状态: ${step1:status} 金额: ${step1:amount} ``` --- ## 执行流程 ``` 1. 加载 YAML → 解析为 YamlToolDef 2. 解析 LLM 参数 → params HashMap 3. 加载 env 声明的环境变量 → env HashMap 4. 初始化 cache = {} 5. step_idx = 0 while step_idx < steps.len(): ┌─ 当前步骤执行 ──────────────────────────────┐ │ │ │ [cache: true?] │ │ ├─ 缓存命中(所有 var 键都存在)→ 跳过HTTP │ │ └─ 缓存未命中 → 继续 │ │ │ │ 展开 url/headers/body 中的模板 │ │ ${param:key} → params["key"] │ │ ${env:VAR} → env["VAR"] │ │ ${stepN:var} → ctx.vars[(N, var)] │ │ │ │ HttpRequest 执行 → HttpResponse │ │ 保存到 ctx.steps[step_num] │ │ │ │ for each var: │ │ extract 语法分发: │ │ ├─ /.../ → 正则引擎,取捕获组1 │ │ └─ 其他 → 路径引擎(JSON→YAML 自动检测) │ │ ├─ 成功 → 存入 ctx.vars + step_vars │ │ └─ 失败 → │ │ ├─ abort:true → 终止,返回错误 │ │ ├─ default:x → 使用默认值 │ │ ├─ jump:N → step_idx = N, break │ │ └─ (无) → 空字符串 │ │ │ │ [cache: true?] → 将 step_vars 存入 cache │ │ │ │ [result 存在?] │ │ └─ 展开 result 模板 → 返回给 LLM → 结束 │ │ │ │ [next 存在?] │ │ ├─ next:N → step_idx = N │ │ └─ (无) → step_idx += 1 │ │ │ └──────────────────────────────────────────────┘ 6. 循环结束 → 返回最后一步原始响应体 ``` --- ## 错误处理 | 场景 | 处理方式 | |------|---------| | YAML 文件格式错误 | 启动时打印警告,跳过该文件 | | 模板引用了未定义的参数 | 替换为空字符串,记录警告日志 | | 环境变量未设置 | 替换为空字符串,记录警告日志 | | HTTP 请求失败(连接/超时) | 返回错误详情给 LLM | | `var.abort: true` 且提取失败 | 终止,返回 "变量 xxx 提取失败" | | `var.jump: N` 且提取失败 | 跳转到第 N 个步骤,N 从 0 开始,不报错 | | `var.default` 且提取失败 | 使用默认值,不报错 | | 路径语法错误(空段、非法索引) | 返回错误给 LLM,附带路径表达式 | | 路径键不存在 / 索引越界 | 提取失败,触发 default/abort/jump | | 正则语法错误 | 返回错误给 LLM,附带正则表达式 | | 正则无匹配 | 提取失败,触发 default/abort/jump | | 响应体非 JSON 也非 YAML(路径引擎) | 返回错误给 LLM | | `next` / `jump` 目标步骤不存在 | 终止,返回配置错误 | --- ## 扩展预留 以下字段预留供后续版本实现: | 字段 | 位置 | 用途 | |------|------|------| | `retry` | `steps[]` | 失败重试次数与退避策略 | | `cache.ttl` | `steps[]` | 缓存有效期(秒) | | `validate` | `params[]` | 参数级正则校验 | | `sensitive` | `env[]` | 标记敏感环境变量,日志脱敏 | | `loop` | `steps[]` | 条件循环(分页自动翻页) | | `export` | `steps[]` | 将变量导出为环境变量供子进程使用 | | `pre_request` | 顶层 | 工具级别的前置请求(全局认证) |