六个步骤:开始 → 拆页 → 校验 → 提交 → 轮询 → 取链接。 只有两步是插件,其余四步是代码和判断。 插件在平台上是一份说明书,不是一段代码。说明书写错,节点连保存都过不去。 同一个链接换输入跑两次,一次通、一次被拦——这就是「测一次」不够的原因。
这一节对应书上六个小节:创建工作流、配置输入节点、集成 PPT 插件、生成 PPT 任务配置、 获取 PPT 文件链接、测试运行。 六小节按顺序读下来会觉得每一步都很小。 真正的难点在于它们之间的顺序:校验在提交前还是后,决定了你浪不浪费一次生成。 本沙箱没有扣子账号也没有 PPT 插件,这一节用本机起的真 HTTP 服务模拟插件的三个接口。 请求是真发的,返回是真解析的,只是对面不是扣子。
一、第一步:工作流的骨架
| 步 | 节点类型 | 干什么 |
|---|---|---|
| 1 | 开始 | 接大纲、模板、页数三个参数 |
| 2 | 代码 | 把大纲拆成页,数一遍 |
| 3 | 选择器 | 超限就走错误出口 |
| 4 | 插件 | 提交任务,拿 task_id |
| 5 | 代码 | 轮询状态,直到 done |
| 6 | 插件 | 拿 task_id 换链接 |
| 7 | 结束 | 把链接和有效期交出去 |
六个业务节点加两个结束节点(一个成功、一个错误)。两个结束节点不是冗余,是让错误有自己的出口。 只留一个结束节点会怎样?错误也得从成功那个口出去,下游分不清手里拿的是链接还是错误。 这一章整条链是直线加一个分叉。比第 12 章简单——那个案例有三分支。复杂的地方不在分支数,在异步。
二、第二步:输入节点要什么
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| outline | string | 是 | 大纲全文 |
| template | string | 否 | 模板名,不填用默认 |
| pages | integer | 否 | 期望页数,只做参考 |
三个参数里只有大纲是必填的。 模板和页数都是「可选但不该省」——不填模板,出来的是默认版式,用户还得再套一遍公司模板。 页数那一项容易误会:它不是「必须生成这么多页」,是「大概这么多页」。 大纲有 6 页,非要 8 页,插件会自己加过渡页。 参数名字要用英文。 中文参数名在部分渠道上会被编码坏掉—— 这和前面讲的 urlencode 是同一个坑的两头。
三、第三步:插件是说明书,不是代码
平台上配插件,填的是名字、描述、地址、参数表四样。保存的时候平台会检查一遍,缺一样就过不去。 常见的错:描述留空、参数类型写成 text(只能是 string / integer / number / boolean)、 地址没写 http 开头。 为什么描述必填? 因为模型要读它。插件是给模型用的,它靠描述判断「什么时候该调这个」。 描述空着,模型就永远不会主动调它。
参数类型写错更直接:类型不认识,平台保存时报错。这一关拦在配置阶段,不会留到运行阶段才炸。
四、第四步:提交任务的三个细节
| 细节 | 做错会怎样 | 正确做法 |
|---|---|---|
| 参数编码 | URL 拼不合法,返回状态 0 | 中文和空格先 urlencode |
| 超时设置 | 默认太短,提交请求被掐断 | 提交接口设 15 秒以上 |
| 返回解析 | 把整个响应当字符串塞下去 | 取 task_id 一个字段 |
第一条最容易踩,而且症状最迷惑:状态 0、没有错误信息,你会先怀疑网络、再怀疑防火墙, 最后才想到是拼串的问题。 第二条:提交请求本身很快(就是排个队),但网络差的时候也可能要好几秒。默认超时往往只有 5 秒。 第三条是习惯问题:下游只要一个 task_id,别把整个 JSON 往下传。传得越多,后面越难改。
五、第五步:轮询要写成循环,不是写成一次
平台上做轮询有两种写法:一种是用循环节点套一个「查状态」,设最大循环次数; 一种是把它拆成两轮对话,这轮提交、下轮再问。 这一章用第一种。循环节点里要设三样:最大次数、每次间隔、退出条件。 退出条件不能只写「等于 done」。 要写「等于 done 或 failed 或次数用完」。 少写一个,循环就会空转到次数上限。
最大次数按前面的算法给:45 秒除 3 秒是 15 次,留点余量设 20 次。 设 100 次只会让超时来得更晚、账单更贵。
六、第六步:取链接和测试运行
取链接这一步很简单:拿 task_id 换一个 URL,顺便拿一个有效期。 有效期要一起取回来。 只取 URL 不取有效期,回复里就写不出「24 小时内有效」, 用户第二天点开是 404。 测试运行要跑两遍,换输入跑:一遍大纲合规、一遍大纲超限。
第一遍看它有没有走到取链接、拿回的 url 是不是空的;第二遍看它有没有在提交前就被拦住。 只跑第一遍,你永远不知道拦截逻辑有没有生效。 跑完还要看一眼执行轨迹:每一步的耗时。 真机上第 4 步应该是几十秒,如果显示 0.1 秒,那多半是没真发出请求。
七、动手 Demo:六个步骤连成一条链
先用一份写坏的接口描述过一遍平台校验,再真发三次 HTTP 请求。 最后把六个节点连成一张链,用两份大纲跑两遍:一遍通、一遍被拦。
PLUG = Q.spec(u'ppt-maker', u'按大纲生成 PPT,异步任务',
BASE + u'/ppt/create',
[(u'outline', u'string', True, u'大纲全文'),
(u'template', u'string', False, u'模板名'),
(u'pages', u'integer', False, u'期望页数')])
code, data = Q.call_plugin(PLUG, arg={u'outline': quote(OUTLINE, safe=u''),
u'template': u'corp-blue'})
实跑输出:
--------------------------------------------------------------------
六个步骤连成一条链
--------------------------------------------------------------------
① 六步各是什么
--------------------------------------------------------------------
| 步 | 类型 | 干什么 |
|---:|---|---|
| 1 | 开始 | 接大纲、模板、页数三个参数 |
| 2 | 代码 | 把大纲拆成页,数一遍 |
| 3 | 选择器 | 页数或要点超限就返回错误 |
| 4 | 插件 | 提交任务,拿 task_id |
| 5 | 代码 | 轮询状态,直到 done 或失败 |
| 6 | 插件 | 拿 task_id 换下载链接,交给结束节点 |
六步里只有两步是插件,其余四步都是代码和判断。这条链的重心不在调插件,在它前后。
第 3 步最容易被省。省了之后,大纲有问题也照样提交,生成出来再退——白花一次生成的时间。
② 插件接口先过一遍校验
--------------------------------------------------------------------
一份写坏的接口描述,过不了平台保存时的检查:缺 description、url 不是 http 开头、
参数 outline 类型不认识。
两条错:描述是空的、参数类型写了 text(只能是 string / integer / number / boolean)。
好的那份:3 个参数,outline(string)、template(string)、pages(integer)。
插件在平台上是一份说明书,不是一段代码。 说明书写错,节点连保存都过不去。
③ 真发一次请求
--------------------------------------------------------------------
提交任务:状态 200,拿回 task_id=T20261031、state=pending。
问一次状态:状态 200,state=done,pages=8。
换链接:状态 200,url=https://files.example.com/t/ppt.pptx,有效期 24 小时。
三次都是真的 HTTP 请求,走的是本机 http://127.0.0.1:65341 上起的服务。
注意参数那一行:中文和空格必须先用 urlencode 编码再拼。
不编码的话,这条 URL 在客户端就拼不合法,返回的状态是 0——你会以为是网络问题,其实是拼串的问题。
④ 跑一遍:大纲改干净之后
--------------------------------------------------------------------
| 步 | 节点 | 类型 | 输出端口 | 毫秒 |
|---:|---|---|---|---:|
| 1 | 开始 | 开始 | (无输出) | 0.0 |
| 2 | 拆页 | 代码 | count、pages | 0.0 |
| 3 | 校验规则 | 选择器 | bad、ok | 0.0 |
| 4 | 提交任务 | 插件 | code、task | 15.7 |
| 5 | 返回错误 | 结束 | 跳过 | — |
| 6 | 轮询状态 | 代码 | code、state | 16.0 |
| 7 | 取链接 | 插件 | code、expire、url | 2.1 |
| 8 | 返回链接 | 结束 | (无输出) | 0.0 |
八步全跑通,一共 34.1 毫秒——因为三次请求都在本机。
结束节点拿到的是:url=https://files.example.com/t/ppt.pptx,有效期 24 小时。
真机上第 4 步要几十秒、第 5 步要轮询十几轮。这段耗时不在本机,所以这里量不出来。
⑤ 大纲没改干净的那次
--------------------------------------------------------------------
| 步 | 节点 | 类型 | 输出端口 | 毫秒 |
|---:|---|---|---|---:|
| 1 | 开始 | 开始 | (无输出) | 0.0 |
| 2 | 拆页 | 代码 | count、pages | 0.0 |
| 3 | 校验规则 | 选择器 | bad、ok | 0.0 |
| 4 | 提交任务 | 插件 | 跳过 | — |
| 5 | 返回错误 | 结束 | (无输出) | 0.0 |
| 6 | 轮询状态 | 代码 | 跳过 | — |
| 7 | 取链接 | 插件 | 跳过 | — |
| 8 | 返回链接 | 结束 | 跳过 | — |
第 3 步返回 ok=False,走 no 分支直接到「返回错误」,后面提交、轮询、取链接全跳过。
一次拦截省下一次生成的时间。 按 45 秒算,一天拦十次就是七分半。
! 两次跑的是同一张链,只换了输入。这就是「测一次」不够、要「换输入测」的原因。
①六步表:只有两步插件,其余四步是代码和判断。 ②写坏的接口描述报三条错:缺描述、url 不是 http、参数类型不认识。 ③真发三次请求全 200:提交拿 task_id、问状态 done、换链接拿到 url。 ④大纲改干净那次八步全通,35 毫秒——三次请求都在本机。 ⑤同一张链换没改干净的大纲,第 3 步就拦下,后五步全跳过。
30 秒实验
- 把你们的 PPT 模板名和页数做成可选参数,允许用户不填。看下游会不会因为空值炸。
- 故意把参数里的中文不编码发一次,看返回什么。记住这个症状,下次就不用查半天。
- 把循环的最大次数从 20 改成 3,看超时提示会不会出现。出现了,说明兜底是通的。
常见误区
- 「插件就是一段代码。」它是一份说明书,模型靠它决定什么时候调。描述不能空。
- 「轮询写一次就够了。」要写成循环,退出条件还得包含 failed 和次数用完。
- 「跑通一次就能发。」换输入再跑一遍。只跑一次,拦截逻辑有没有生效你不知道。
自测三问(点开对答案)
1. 为什么要两个结束节点? —— 让错误有自己的出口。只留一个,下游分不清手里拿的是链接还是错误。 2. 轮询的退出条件要写几样? —— 三样:done、failed、次数用完。少写一个,循环会空转到上限。 3. 测试运行为什么要跑两遍? —— 一遍合规的看通不通,一遍超限的看拦不拦得住。只跑一遍测不出拦截。链跑通了,下一节讲质量:链接、文件名、还有哪几项能自动验。
- ☐ (本篇完,下一篇 14.4 应用优化与质量控制要点)
相关笔记
- 应用架构设计:上一节的架构
- 如何使用插件:插件的参数和校验
- 应用优化与质量控制要点:下一节质量控制
- 🐦 扣子开发 AI Agent · 索引