自动化脚本擅长做确定的事:点击固定的坐标、读取稳定的控件、按预设分支执行。但现实里的自动化流程往往卡在"不确定"上------通知文本千变万化、用户意图需要理解、页面结构偶尔漂移。把大语言模型嵌进脚本,解决的就是这类需要语义判断的环节。冰狐平台提供了原生的 BigModel 对象,调用方式比手动拼 HTTP 请求、处理签名要直接得多。下面从接口本身出发,写清楚在冰狐自动化脚本中怎样调用大模型,代码会给出几个不同层次的样例,最后讨论几个实际跑起来才会遇到的问题。
接口边界:BigModel 和 invoke 做了什么
冰狐对 LLM 的封装只有两个核心入口:BigModel 构造函数和实例的 invoke 方法。构造函数接收四个参数:
new BigModel(type, custom_data_key, custom_data_secret, options)
type 是一个整数,决定调哪家模型:百度千帆为 1,DeepSeek 为 2,豆包为 3,千问为 4。custom_data_key 和 custom_data_secret 并不是直接填入 API Key 字符串,而是冰狐自定义数据中存储密钥的键名。脚本运行时由平台按名称去读取,密钥不会出现在脚本正文里。这一点很关键:如果把密钥硬编码在脚本中,一旦脚本被分享或导出,泄露就是附带发生的;走自定义数据的话,换模型、换密钥只需要在后台改,脚本本身不用动。
options 是可选对象,默认值为 {model:'', temperature:0.7, maxTokens:1024}。temperature 影响输出的随机性,值越低越趋于保守和确定;maxTokens 限制单次返回的长度。对于自动化脚本里的判断类调用,通常应该把 temperature 压低,比如 0.2 到 0.4,让模型倾向于给出稳定、可预期的答案,而不是每次措辞都变。
invoke 接收一个 prompt,返回字符串。prompt 有两种形态:字符串用于单轮问答;数组用于带角色设定的多轮对话,数组元素支持 system、user、assistant 三种键。system 用来固定模型的角色,user是提问,assistant 可以塞入历史回复来维持上下文。返回值是纯字符串,拿到之后可以直接用于后续的字符串判断、日志输出,或者作为自动化操作的决策依据。
前置配置:密钥放进自定义数据
在写代码之前,需要先在冰狐后台完成两件事。第一,去目标模型的官方平台申请凭证:百度千帆需要 API Key 和 Secret Key;DeepSeek 只需要 API Key,custom_data_secret 可以留空;豆包和千问按各自平台的流程申请。第二,在冰狐后台的自定义数据模块中创建对应的键值条目。如果统一使用 custom_data_key 和 custom_data_secret 两个名称,脚本里就可以复用同一套参数名,切换模型时只改 type 和凭证内容。
样例一:最小可运行的单轮调用
先看一个没有额外封装的版本,确认调用链路是通的。
function main() {
var bm = new BigModel(2, 'custom_data_key', '', {
temperature: 0.2,
maxTokens: 256
});
var ret = bm.invoke('用一句话说明自动化脚本适合处理哪类任务。');
console.log('模型返回:', ret);
if (ret && ret.length > 0) {
console.log('调用成功,返回长度:', ret.length);
}
}
这段代码选择 DeepSeek(type=2),custom_data_secret 传空字符串,因为 DeepSeek 不需要 secret。temperature 设为 0.2,目的是让回答趋于稳定;maxTokens 限制在 256,避免返回过长文本拖慢脚本后续步骤。ret 拿到后先做非空判断,再决定是否继续,这是脚本里处理外部调用返回值的基本习惯。
如果换成百度千帆,只需要把构造函数改为 new BigModel(1, 'custom_data_key', 'custom_data_secret', {...}),前提是后台已经填好了对应的 Key 和 Secret。
样例二:带系统角色的数组式调用
自动化脚本里的大模型调用往往不是"随便聊一句",而是要求模型站在某个角度给出可解析的结果。system字段承担的就是这个职责。
function main() {
var bm = new BigModel(3, 'custom_data_key', 'custom_data_secret', {
model: 'doubao-pro',
temperature: 0.3,
maxTokens: 512
});
var messages = [
{ system: '你是一个通知分类器。只输出一个类别词,不要解释。可选类别:好友消息、群消息、公众号通知、系统通知。' },
{ user: '【张三】在“项目A群”中发送:明天的评审改到下午三点。' }
];
var category = bm.invoke(messages);
console.log('分类结果:', category);
}
这里选择豆包(type=3),并通过 options.model 指定了 doubao-pro。system 指令要求只输出类别词,user 给出待分类的文本。拿到返回值后,脚本可以直接用 category 做条件分支,比如好友消息走一套回复逻辑,群消息走另一套。需要注意的是,即使 system 写了"只输出类别词",模型偶尔仍可能多吐一两个字。所以在真正依赖返回值做硬分支之前,通常还要对 category 做一次 trim 或包含匹配,而不是完全信任它的格式。
样例三:把调用封装成带兜底的函数
脚本运行环境和模型服务之间隔着网络,invoke 可能因为超时、鉴权配置错误或服务端临时故障而失败。直接在主流程里裸调,一处失败会让整个自动化流程卡住。把调用包进函数,加上尝试和退路,是更实际的做法。
function callModelSafely(promptText, fallback) {
var maxAttempts = 2;
var lastError = '';
for (var i = 0; i < maxAttempts; i++) {
try {
var bm = new BigModel(2, 'custom_data_key', '', {
temperature: 0.2,
maxTokens: 300
});
var result = bm.invoke(promptText);
if (result && result.length > 0) {
return result;
}
lastError = 'empty response';
} catch (e) {
lastError = '' + e;
console.log('第' + (i + 1) + '次调用失败:', lastError);
}
}
console.log('大模型调用最终失败,使用兜底值。最后错误:', lastError);
return fallback;
}
function main() {
var text = '判断这句话的情感倾向,只回答“正面”或“负面”:这个版本的更新比上个版本稳定多了。';
var sentiment = callModelSafely(text, '未知');
console.log('情感判断:', sentiment);
if (sentiment.indexOf('正面') >= 0) {
console.log('进入正面处理分支');
} else if (sentiment.indexOf('负面') >= 0) {
console.log('进入负面处理分支');
} else {
console.log('无法判断,走默认流程');
}
}
冰狐 JS 支持 try...catch,可以用它来捕获 invoke 抛出的异常。这里做了两次尝试,失败后返回调用方指定的兜底值,而不是让 undefined 或空字符串流入后续逻辑。自动化脚本最怕的不是"模型答得不好",而是"调用挂了之后流程还在拿空值继续跑"。兜底值把失败变成一个显式的、可处理的状态。
样例四:调用结果直接驱动自动化操作
大模型在脚本里的价值,不在于它自己说了什么,而在于它的输出能触发什么。下面这个结构把通知监听、模型分析和后续动作串起来,是一个更贴近实际使用的形态。
var __global lastNotice = '';
function main() {
console.log('开始监听通知,准备进行语义判断');
return 'listening';
}
function cbNotification(textList, className, packageName, rawEvent) {
if (packageName != 'com.tencent.mm') {
return;
}
var content = textList.join(',');
if (content.length == 0) {
return;
}
var category = classifyNotice(content);
console.log('通知类别:', category);
if (category.indexOf('紧急') >= 0) {
sendMessage('notice_alert', content);
}
}
function classifyNotice(text) {
var prompt = '将以下通知归为三类之一:紧急、普通、忽略。只输出类别词。通知内容:' + text;
var fallback = '普通';
try {
var bm = new BigModel(3, 'custom_data_key', 'custom_data_secret', {
temperature: 0.2,
maxTokens: 32
});
var ret = bm.invoke(prompt);
if (ret && (ret.indexOf('紧急') >= 0 || ret.indexOf('普通') >= 0 || ret.indexOf('忽略') >= 0)) {
return ret;
}
return fallback;
} catch (e) {
console.log('分类调用异常:', '' + e);
return fallback;
}
}
cbNotification 是冰狐的通知回调,__global 用于跨线程共享数据。classifyNotice 把模型调用和格式校验放在一起:如果返回内容包含预设的三个类别之一,就采用;否则回退到"普通"。这种写法把模型的"不确定输出"收窄到一个可控的枚举范围内,后面的自动化逻辑才能放心地用 indexOf 做分支。
几个实际会碰到的问题
temperature 不是装饰参数。 如果调用模型是为了做判断、分类、提取字段,temperature 应该压到 0.3以下。高 temperature 会让同一个输入在不同次调用中给出措辞不同的答案,而脚本里的字符串匹配逻辑是按固定关键词写的,措辞一飘就可能漏判。
重试要有上限,并且区分错误类型。 上面的 callModelSafely 做了两次尝试,没有无限循环。网络抖动导致的超时值得重试,但鉴权配置错误------比如自定义数据键名写错、密钥过期------重试再多次也不会变好,反而拉长脚本卡住的时间。在实际脚本里,可以在 catch 分支中检查错误信息是否包含鉴权相关关键词,如果是,直接走兜底,不再重试。
并发调用需要自己控制节奏。 冰狐脚本支持 Thread 和 Lock,多线程确实可以同时发起多个模型请求,但模型服务端通常有频率限制。在脚本里最稳妥的做法是串行调用,或者用一个简单的间隔控制:上一轮 invoke 返回后,setTimeout 延迟一小段时间再发下一轮。除非明确知道所用模型的并发额度,否则不要一上来就开多线程批量打。
返回值的格式不能默认是干净的。 即使 system 里写了"只输出类别词",模型仍然可能带上标点、空格或者一句简短的解释。脚本侧的对策不是反复修改 prompt 到完美,而是在接收端做一次清洗和容错:trim、indexOf、正则提取,三者叠加使用,比单纯依赖模型"听话"更可靠。
冰狐的 BigModel 没有试图把大模型包装成一个万能的黑盒,它暴露的东西很少:选模型、配参数、发 prompt、拿字符串。这种设计的代价是脚本作者需要自己处理返回值的不确定性和调用的失败路径,但好处也很明显------大模型在自动化流程里只是一个可以被兜底、可以被绕过、可以被替换的环节,而不是一个一旦挂掉整个脚本就跟着停摆的单点。把这一点想清楚,后面的代码结构自然就是围绕"调用可能失败"和"返回需要校验"来组织的,而不是围绕"模型很聪明"来写的。