aardio 文档

aardio 调用 AI 大模型

一. 基本用法

本节的完整范例源代码

  1. 创建 AI 客户端

    import web.rest.aiChat;
    var aiClient = web.rest.aiChat({
        key = '这里指定 API 密钥';
        url = "这里指定大模型接口地址";
        model = "这里指定模型名称"; //llama.cpp 本地模型可以省略
        temperature = 0.1; //可选指定温度 
        maxTokens = 1024; //可选指定最大回复长度
        protocol = null; //可选指定 API 接口协议类型
        extraParameters = {}; //可选指定 extraParameters 属性值
    } )
    
  2. 创建聊天消息对列,保存对话上下文。

    var msg = web.rest.aiChat.message();
    
    //可调用 msg.system() 函数添加系统提示词。
    msg.system("你是桌面智能助手。");
    
    //添加用户提示词
    msg.prompt( "请输入问题:" );
    

    也可以用模拟 AI 的角色添加回复到消息对列

    //模拟 AI 角色
    msg.assistant("请输入问题:" );
    

    这样自问自答的历史消息可以起到小样本学习的作用,让 AI 后面的回复更符合要求,小样本学习的效果有时候会非常好。

  3. 向 AI 服务器发送请求,接收 AI 回复

    var resp,err = aiClient.messages(msg,
        function(deltaText,reasoning,reasoningDetailsType){
    
            /*
            reasoning 可能为 null 或字符串。
            推理模型会首先通过 reasoning 参数输出推理过程(同样是增量字符串),同时 deltaText 为空字符串 "" 。
            reasoningDetailsType 默认为 null,如果 reasoning 来自 reasoning_details,则 reasoningDetailsType 的值为 "text" 或 "summary" 之一。范例代码中通常会省略 reasoningDetailsType 参数,一般不必处理。
            在工具调用后 reasoning_details 会自动回传到服务器。
            */
            if(reasoning) return;
    
            //回复完成则 为 null 。
            console.writeText(deltaText)
    
            //如果需要输入增量输入到目标窗口
            //key.sendString(deltaText)
    
            //显示为屏幕汽泡提示,支持增量文本。
            //winex.tooltip.popupDelta(deltaText) 
        }
    );
    

    调用 ai.messages 就开始对话,如果参数 @2 指定了 SSE 流式回调函数,就自动切换到 SSE 流式调用( 打字效果, 逐步渐进式回复增量文本 ),服务器每次发送增量文本都会传入 deltaText 参数,当 AI 回复结束时 deltaText 为 null 。

    aiClient.messages 有两种不同的用法,返回值也有所不同:

    如果请求失败则返回值 respnullfalse 等非真值,而返回值 err 包含可能的错误字符串。web.rest.aiChat 继承自 web.rest.client,所以也可以用 aiClient.lastResponseError() 获取错误对象(解析 JSON 格式错误信息得到的对象),用 aiClient.lastResponseString() 获取服务器的原始错误信息(字符串对象),或者用 aiClient.lastStatusCode 获取 HTTP 响应状态码。更多用法请参考 使用 web.rest 客户端

本节的完整范例源代码

二. 兼容接口

1. OpenAI 兼容接口。

web.rest.aiChat 默认使用 OpenAI 兼容接口。 基本上大部分大模型都使用 OpenAI 兼容接口,例如 DeepSeek 。

调用 OpenAI 流式接口示例:

import console.int; 
console.showLoading(" Thinking "); 

//1. 创建 AI 客户端
//---------------------------------------------------------------------
import web.rest.aiChat;
var aiClient = web.rest.aiChat(
    key =   'YOUR_API_KEY';
    url = "https://api.deepseek.com/v1";
    model = "deepseek-v4-pro";
    temperature = 1;
    reasoning = {effort: "high"}; // "none" 关闭思考
    //maxTokens = 4096;
)

//2. 创建消息队列
//---------------------------------------------------------------------
var msg = web.rest.aiChat.message();
msg.prompt( "请介绍你自己" );

//3. 第三步:发送请求。
var resp,err = aiClient.messages(msg,
    function(deltaText,reasoning){

        if(reasoning) {
            return console.writeColorText(reasoning,0xA);
        }

        //回复完成则 为 null 。
        console.writeText(deltaText) 
    }
);

console.error(err);

OpenAI 的 GPT5.x 系列通常不会主动输出思考与调用工具的过程,可以在系统提示词中添加内容:在深度思考与每次调用工具前要输出 1-2 句可显示的“前导语”(Preamble),告知用户你接下来你准备干什么。

OpenAI 缓存透传

OpenAI 支持自动前缀缓存,通常不需要额外设置参数。

但是,通过中转服务商请求 OpenAI 接口可能出现缓存功能失效,这是因为中转服务商可能轮换了不同的 key 发送请求。这会导致费用大幅增加。

解决方法是在自定义参数中显式指定 prompt_cache_keyprompt_cache_retention 字段。

示例:

var aiClient = web.rest.aiChat(
    key =  'API_KEY';
    url = "https://api.fenno.ai/v1";
    model = "gpt-5.5";
    temperature = 0.2;
    reasoning = {effort = "high"};
    extraParameters  = {
        prompt_cache_key = "your_unique_session_id_12345";
        prompt_cache_retention =  "24h";
    };
)

注意如果多用户共享相同的 prompt_cache_key 会被路由到固定的 GPU 机器,如果同一缓存键并发请求过多(通常超 15次/分钟),系统会触发溢出路由,从而降低缓存效果。因此应当避免多用户使用相同的 prompt_cache_key。

对于中转服务商,可以考虑对 API key 加盐然后计算 sha256 哈希作为 prompt_cache_key,例如: prompt_cache_key = crypt.sha256( config.key ++ io._exepath )

OpenAI 的提示缓存(Prompt Caching)是完全免费写入的,因此将 prompt_cache_retention 设为 "24h" 不会增加额外费用,反而可大幅降低成本与延迟。

2. Vertex / Gemini 接口

支持 OpenAI 兼容接口:

也可以支持 Google 自家的协议接口:

Vertex 密钥设置: #

使用 Vertex 接口时 web.rest.aiChat 构造参数表中的 key 字段需要指定 GCP 密钥数据。

打开「 Vertex AI 控制台 » 主菜单 » IAM 和管理 » 服务账号」 创建并下载 JSON 格式密钥 。

GCP 密钥数据应当是一个表对象或者 JSON 格式字符串( 密钥的第一个字符必须是 { )。

GCP 密钥数据主要字段说明:

如果 key 指定 GCP 密钥数据则 web.rest.aiChat 会自动获取 GCP 访问令牌,并且默认会跨线程缓存访问令牌以避免重复获取令牌。也可以自行调用标准库 web.rest.gcp.jwtBearerToken 提前获取访问令牌。

自定义思考配置:

示例:

import web.rest.aiChat;
var aiClient = web.rest.aiChat(
    key =   "密钥";
    url = "https://generativelanguage.googleapis.com/v1beta/";
    model = "gemini-3-flash-preview"
    reasoning = {effort = "high"}
)

如果 reasoning.exclude 不为 true 则输出思考过程。

Gemini 2.5 可使用 reasoning.maxTokens 设置推理时允许消耗的 tokens 上限,设 为 0 则关闭思考,设为 -1 则按需动态设置。 Gemini 3.x 则应使用 reasoning.effort 替代 reasoning.maxTokens 。

示例:

例如用 aardio 向 AI Studio 接口 https//:generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key=YOUR_API_KEY 发送请求的代码如下:

import web.rest.aiChat;

// 1. 创建 AI 客户端。
var aiClient = web.rest.aiChat(
    key = "YOUR_API_KEY"; 
    url = "https://generativelanguage.googleapis.com/v1beta"; 
    model = "gemini-3.5-flash"; 
    //proxy = "socks=127.0.0.1:1081"; //代理服务器 
    //protocol = "google"; //generativelanguage.googleapis.com/v1beta 默认使用 Google 协议
);

// 2. 创建消息队列
var msg = web.rest.aiChat.message();
msg.prompt("你好,请用中文介绍一下你自己。");

// 3. 发送请求,如果没有提供第二个回调函数参数,则会禁用流式回复并等待服务器返回完整结果
var resp, err = aiClient.messages(msg); 
print(resp.candidates[1].content.parts[1].text); //Google 协议返回的数据结构与 OpenAI 不同

Gemini 3.x 系列模型的 temperature 只能设为 1,不建议改动

3. Anthropic 接口

import web.rest.aiChat;
var aiClient = web.rest.aiChat(    
    key = '密钥';
    url = "https://api.deepseek.com/anthropic";
    model = "deepseek-v4-flash";
    protocol = "anthropic";//指定使用 Anthropic 接口协议,也就是 Claude 大模型官网接口
    reasoning = { // 不指定 reasoning 则使用默认值
        effort = "max"; //设为 "none" 关闭思考
    }
)

如果接口网址包含 anthropic 也会自动切换为 Anthropic 协议(可以省略 protocol 字段)。

4. Ollama 接口

Ollama 本地模型在 aardio 或 ImTip 中的接口地址写以下任何一个都可以:

http://localhost:11434
http://localhost:11434/v1/
http://localhost:11434/api/

Ollama 只要填网址和模型名称,key不需要指定。

请参考: 自动部署本地 Ollama 模型

5. OpenRouter 接口

OpenRouter 使用的是 OpenAI 接口,所以不需要指定 protocol。

OpenRouter 的思考模型可通过 reasoning 字段指定推理参数,示例:

var aiClient = web.rest.aiChat({
    key = "api-key";
    url = "https://openrouter.ai/api/v1";
    model ="x-ai/grok-code-fast-1";
    temperature = 0.1;
    reasoning = { 
        effort: "high"
        //exclude = true;
    }
})

如果指定了 exclude = true 则不会回显思考过程,一些模型通过 reasoning.effort 指定思考强度(例如 grok-code-fast-1 ), 一些模型则可以通过 reasoning.maxTokens 控制思考强度。

6. 魔搭接口

魔搭使用 OpenAI 接口,示例:

aiClient = web.rest.aiChat(    
    key =  "密钥";
    url = "https://api-inference.modelscope.cn/v1";
    model = "deepseek-ai/DeepSeek-V3.1";
    temperature = 0.1;
    extraParameters = {
        enable_thinking = true;
    };
)   

使用 extraParameters.enable_thinking 开始思考模式。

7. 七牛云 AI 大模型推理接口

示例:

var aiClient = web.rest.aiChat(
    key = '密钥';
    url = "https://api.qnaigc.com/v1"; 
    model = "z-ai/glm-5";
    temperature = 0.5;
    thinking = { type = "disabled" } 
    //protocol = "anthropic" //可选切换为 anthropic 协议
)

七牛云的接口有多个:

七牛云接口部分模型支持用 reasoning.effort 或 thinking.type , thinking.budget_tokens 自定义思考模型或强度。或者直接用 extraParameters.thinkingextraParameters.reasoning_effort 指定附加参数也可以。

8. 智谱接口

示例:

var aiClient = web.rest.aiChat( {
    key = "密钥";
    url = "https://open.bigmodel.cn/api/paas/v4";
    model = "glm-5.1";
    temperature = 0.5;
    thinking = { type = "disabled" } 
})  

参数指定 thinking = { type = "disabled" } 关闭思考。
如果不指定 thinking 字段则默认开启思考(或指定为 thinking = { type = "enabled" } 显式启用思考)。

智谱 GLM 5.1 的 temperature 参数不能设置得太小,例如设为 0.1 能力会显著退化

9. 火山平台大模型与智能体接口

火山方舟平台大模型(豆包、DeeepSeek 等)的接口以及智能体接口都兼容 OpenAI 接口。火山智能体的的接口地址为 https://ark.cn-beijing.volces.com/api/v3/bots,大模型接口地址为 https://ark.cn-beijing.volces.com/api/v3, 模型 ID 参数可以填模型 ID 也可以填智能体应用的 ID。 示例:

import web.rest.aiChat;
var aiClient = web.rest.aiChat(    
    key = '密钥';
    url = "https://ark.cn-beijing.volces.com/api/v3/bots"; //如不是智能体就去掉 "bots"
    model = "bot-20250115093718-r9gcj"; //模型或智能体 ID
    temperature = 0.1; //温度 
)

10. 阿里云 AI 模型与智能体接口

使用 web.rest.aiChat 可以兼容阿里通义千问的大模型接口以及智能体接口。

调用阿里大模型与智能体,API 接口网址只要写 https://dashscope.aliyuncs.com 就可以,然后 model 参数写模型 ID 或者智能体应用 ID。当然你也可以直接写阿里提供的接口网址。

import web.rest.aiChat;
var aiClient = web.rest.aiChat(   
    key = '密钥';
    url = "https://dashscope.aliyuncs.com";
    model = "qwen-coder-plus"; //这里写模型 ID 或者智能体应用 ID,aardio 会自动兼容
    temperature = 0.1;
    maxTokens = 1024,
)

三. AI 调用本地函数( Function calling ) #

1. 交错思考(Interleaved Thinking) #

所谓交错思考指的是大模型在开启思考模式以后,一边思考(推理)一边调用工具,在一次用户对话中思考与调用工具可以交错执行多次。 实际上在一次用户对话过程中会向服务器回传多次请求,而在多次请求时需要回传思考内容(推理过程),而在一次用户对话结束后,通常应当丢弃这些思考内容(推理过程),在对话历史中仅保留 AI 回复的正文,即使用户继续对话上一次的思考过程通常会被丢弃。

而交错思考的实现并没有统一的标准,各家的实现都不一样,web.rest.aiChat 则尽可能地兼容了不同的实现。

最佳实践是:对于使用 web.form.chat 等 AI 对话前端界面,建议由界面线程的 web.form.chat 保存对话记录。 而在工作线程中单独创建新的 web.rest.aiChat 发送对话请求。对于支持交错思考的接口,web.rest.aiChat 会自行维护对话记录,并在对话记录中保存推理过程(思考过程)。因为 aardio 的线程隔离特性,这些推理过程并不会被自动添加到界面线程(例好 web.form.chat 对象的 chatMessage 属性),在工作线程中我们可以手动调用 web.form.chat 的 assistant 方法存储一些必要的工具调用结果到界面线程。在一轮用户对话结束后退出工作线程,这些思考过程将被自然丢弃。

完整的实现请参考 Autos 源码

2. AI 用工具(本地函数)的步骤

注意不是所有大模型接口都支持 function calling 。如果服务端报错缺少 content 字段,这通常是因为接口不支持 function calling,只能处理包含 content 的普通消息。

首先在创建 AI 客户端时,在参数中使用 tools 字段指定允许被 AI 调用(function calling)的本地函数。
tools 应当指定一个数组,数组的每个成员指定一个函数定义,细节可参考调用的大模型相关文档。

示例:

var aiClient = web.rest.aiChat(
    key = "密钥";
    url = "https://api.*****.net/v1";//接口地址
    model = "模型名称"; 
    temperature = 0.5; 
    tools = { //关键在于增加 tools 字段声明可以调用的本地函数,细节请参考 API 文档。
        {
            "type": "function",
            "function": {
                "name": "getWeather",
                "description": "获取给定地点的天气",
                "parameters": {
                    "type": "object",
                    "properties": {
                        "location": {
                            "type": "string",
                            "description": "地点的位置信息,比如北京"
                        },
                        "unit": {
                            "type": "string",
                            "enum": {
                                "摄氏度",
                                "华氏度"
                            }
                        }
                    },
                    "required": {
                        "location"
                    }
                }
            }
        }
    }
)

然后我们需要在 aiClient.external 表里定义允许 AI 调用的同名函数,与前面的 tools 里声明的函数名称与原型说明要匹配。

示例:

//导出允许 AI 调用的函数
aiClient.external = {
    getWeather = function(args){ 

        //如果重复调用相同的函数,是因为模型实际并不支持 function calling
        console.log("正在调用函数,参数:",args.location,args.unit)

        //尽量用自然语言描述清楚
        return  args.location + "天气晴,24~30 度,以自然语言回复不要输出 JSON"
    } 
}

然后创建对话消息队列,示例如下:

//单独 创建 AI 会话消息队列以保存聊天上下文。
var chatMsg = web.rest.aiChat.message();

//添加用户提示词
chatMsg.prompt("杭州天气如何?" );

最后发送请求启动对话:

console.showLoading(" Thinking "); 

//调用聊天接口。
var ok,err = ai.messages(chatMsg,console.writeText);

完整版范例源码

四. AI 续写与补全应用

如果需要更好的效果,则建议在 AI 提示词中添加更多的信息,例如让 AI 知道目标进程的文件名,并要求 AI 根据不同的程序给出更合适的解答,完整示例请参考: 范例 - 超级热键调用 AI 大模型自动续写补全

aardio 基于上面的范例已经内置了 F1 键 AI 助手,运行效果:

F1 键助手

利用 F1 键还可以在 aardio 中调用 AI 写其他编程语言的代码,例如写 Python 代码:

F1 键助手写 Python 代码

在调用 AI 续写补全时,清晰的提示很重要。例如上面我们简明扼要地通过变量命名与注释让 AI 明确 pyCode 里放的是 Python 代码。在编码补全时,在清晰的注释提示后面补全有更好的效果。 注意用法,那么在 aardio 环境中调用 AI 写前端代码、Python 代码、 Go 语言的代码的效果会很好,利用 AI 可以更好地利用 aardio 在混合语言编程上的优势。

五. AI 搜索 #

如果是用于 aardio 编程的 AI 助手推荐使用 aardio 提供的 VIP 专属接口, aardio 提供了专业版知识库,匹配速度更快,也更加智能与准确。

调用 Tavily 搜索接口 #

Tavily 搜索质量不错,而且只要注册账号就可以获取每月搜索 1000 次的免费额度,一般够用(响应比 Exa 慢)。

示例:


//导入 Tavily 搜索接口
import web.rest.jsonClient;
var http = web.rest.jsonClient();
http.setAuthToken("接口密钥");
var tavily = http.api("https://api.tavily.com");

//搜索,不建议指定 include_raw_content 参数( 返回的 raw_content 可能有乱码 ).
var resp = tavily.search(
    query = "aardio 如何读写 JSON",
    max_results = 3, //限制返回结果数,默认值为 5。
    //topic = "news", //限定返回最新数据
    //time_range = "month", //搜索最近一个月发布或更新的内容
    include_domains = ["www.aardio.com"] //可选用这个字段限定搜索的域名数组
)

//创建对话消息队列
import web.rest.aiChat; 
var msg = web.rest.aiChat.message();

//将搜索结果添加到系统提示词
msg.url(resp[["results"]])

//添加用户提示词
msg.prompt( "DeepSeek 有哪些成就" );

请参考tavily 文档

调用 Exa 搜索接口 #

一般需要根据用户的最后一个提示词进行搜索,并将搜索结果添加到最后一个用户提示词之前。

//导入 Exa 搜索接口
import web.rest.jsonClient; 
var exaClient = web.rest.jsonClient(); 
exaClient.setHeaders({ "x-api-key":"接口密钥"} )
var exa = exaClient.api("https://api.exa.ai/");

//搜索
var searchData,err = exa.search({
    query:"DeepSeek 有哪些成就", 
    contents={text= true}
    numResults:2,
    includeDomains:{"www.aardio.com"},//可以在指定网站内搜索
    type:"keyword" //一般 keyword 搜索就够了(价格低一些)
})

//创建对话消息队列
import web.rest.aiChat; 
var msg = web.rest.aiChat.message();

//将搜索结果添加到系统提示词
msg.url(searchData[["results"]])

//添加用户提示词
msg.prompt( "DeepSeek 有哪些成就" );

exa.ai 的搜索质量不错。

调用博查搜索接口 #

import web.rest.aiChat;
var msg = web.rest.aiChat.message();

var bochaClient = web.rest.jsonClient(); 
bochaClient.setAuthToken("接口密钥");

//导入博查搜索接口
var bocha = bochaClient.api("https://api.bochaai.com/v1/{method}-search");

//搜索
var searchData,err = bocha.web({ 
    "query": "DeepSeek 最近有哪些新闻事件",
    "freshness": "noLimit",
    "answer": false,
    "stream": false,
    "count": 2; 
})

//添加到系统提示词
msg.url(searchData[["data"]][["webPages"]][["value"]])

msg.prompt( "DeepSeek 最近有哪些新闻事件" );

六. 在 HTTP 服务端开发 AI 中转接口

HTTP 服务端关键代码如下:

var apiKey = request.headers["authorization"]
if(apiKey) apiKey = string.match(authorization,"\S+\s+(\S+)");

if(!apiKey){
    response.errorStatus(401);
    return;
}

// ...... 其他代码省略

import web.rest.aiChat;

// 获取客户端请求的参数
var requestData = request.postJson()

var aiClient = web.rest.aiChat(    
    key =  'API_KEY'; //上游接口的 APK key
    url = "https://generativelanguage.googleapis.com/v1beta";
    model =  "gemini-3.5-flash";
    temperature = requestData.temperature;
    maxTokens = requestData.max_tokens; 
    tools = requestData.tools;
    reasoning = {
        effort = "high";
        maxTokens = -1;
    }
    protocol = "google";
)

var resp,err = aiClient.messages(requestData.messages,
function(deltaText,reasoning){ 

    if(!(deltaText || #reasoning) ){ 
        response.eventStream({data = {"DONE"}});
        return; 
    }   

    response.eventStream({
        data = {
            choices = {{
                delta = {
                    content = deltaText;
                    reasoning_content = reasoning;
                }
            }}
        }
    });  
});

if(err){
    response.error(err)
}

Markdown 格式