# aardio 范例: AI 智能助手 —— 全自动编程、自动操作电脑

``````aardio
import win.ui;
import time.lunar;
import fonts.fontAwesome;
/*DSG{{*/
var winform = win.form(text="AI 智能助手 —— 全自动编程、自动操作电脑";right=900;bottom=600;bgcolor=0xFFFFFF)
winform.add(
btnSend={cls="plus";text="运行";left=790;top=550;right=885;bottom=590;bgcolor=0xD77800;border={radius=4};db=1;dr=1;font=LOGFONT(h=-14);iconStyle={align="left";font=LOGFONT(h=-13;name='FontAwesome');padding={left=15}};iconText='\uF1D8';notify=1;textPadding={left=15};z=3};
btnSetting={cls="plus";left=11;top=551;right=46;bottom=581;align="left";color=0x3C3C3C;font=LOGFONT(h=-13);iconStyle={font=LOGFONT(h=-22;name='FontAwesome')};iconText='\uF013';textPadding={left=25};z=4};
customChat={cls="custom";text="聊天区域";left=0;top=0;right=900;bottom=540;db=1;dl=1;dr=1;dt=1;z=1};
editPrompt={cls="edit";left=50;top=550;right=780;bottom=590;db=1;dl=1;dr=1;edge=1;multiline=1;z=2}
)
/*}}*/

import fsys.table;
import web.rest.aiChat.settingForm;

// 创建配置文件
var path = io.appData("aardio/std/example/ai/agent4.table");
var config = fsys.table(path);

winform.btnSetting.oncommand = function(id,event){
	
	table.mix(config,{
		title = "设置 AI 智能助手"; // 标题  
		hyperlink1Title = "获取 key"; // key 输入框显示嵌入链接  
		
		//可选预定义默认设置（值必须是对象数组）
		itemData = [{
			url="https://api.deepseek.com/v1";
			key= '';
			model="deepseek-v4-pro";
			temperature=1;
			reasoning={"effort":"max 全力以赴"}
		}] 
	});
	 
		
	//打开 AI 设置窗口，参数 2 指定配置文件
	var frmSetting = web.rest.aiChat.settingForm(winform,config);

	//用户点击了「保存」 			
	if( frmSetting.doModal(this) ){ 
		//获取用户当前选择的配置
		var selectedConfig = config.itemData[config.selItem]; 
	}
}

//创建 AI 聊天界面
import web.form.chat;
var chatUi = web.form.chat(winform.customChat);

var tip = /***
### 警告： 
**将操作电脑的权力交给 AI 存在风险，AI 幻觉可能导致不确定后果。**  
**使用此工具造成的一切后果自负，如不同意请不要使用**  

### 功能：
1. AI 自动编写程序、自动调试、自动操作电脑直到任务执行成功。

### 设置：
请 <a href="javascript:void(0)" onclick="javascript:external.updateApiKey()">» 点这里设置</a> 接口参数，指定支持交错思考 + 工具调用的大模型（例如 Gemini 3 Flash，DeepSeek V4 Pro ）。 

设置 » 推理强度（ [详细说明文档](https://www.aardio.com/zh-cn/doc/library-guide/std/web/rest/aiChat.html#reasoning) ）：
DeepSeek⁴ 建议选择 `max`，Gemini³ Flash 建议设为 `medium`。
Gemini³ 的 temperature 必须指定为 1，DeepSeek 开启思考时直接忽略该值。

请不要在此工具内使用 ai.aardio.com 接口。
[领取 100 亿 tokens 支持 Gemini 3](https://imtip.aardio.com/api-keys/)

其他细节请查看本工具开源代码。

### 技巧：

- 展开「推理过程」可查看 AI 执行成功的源代码
- 按住 `Ctrl` 键点代码可以快速选中代码块，鼠标左键 3 连击也行。

***/
chatUi.write(tip)

chatUi.aardioSystem()

var initSystemPrompt = function(){
	var sys = "

## 角色

你是一个自动编写、调试、完善 aardio 代码的智能编码助手。

## 任务

1. 自动编写、调试、完善 aardio 代码。
2. 调用工具自动执行必要的单元测试（发挥动态语言的特性，验证你的想法与思路的可行性），你甚至可以调用工具单独测试某个库函数或一小段代码，不要怕浪费 tokens ，你只要尽最大能力调用工具，尽最大能力思考即可，不放过任何可以尝试或探索的方法与路径。
3. 调用工具 loadcodex 执行 aardio 代码完成用户的要求（例如编写 aardio 程序或者自动化操作电脑）。
4. 在必要的时候可以在程序中获取运行时的信息（例如利用 gdi.getPixel(x,y,hwnd) 等验证屏幕上输出的颜色点）以验证与改进程序。

## 要求

1. 你是一个精益求精的助手，会尽可能利用可调用的工具以生成更优质更可靠的代码。例如使用 lookup_library 工具以读取本地库文档，以避免遗漏或误用重要的库函数与功能。
2. 你是一个高级的智能助手，拥有强大的编写 aardio 代码、调试 aardio 代码的技能与工具，请利用 loadcode,loadcodex,aifix 这些编译与检查工具自我修正代码。

## 举例

举个例子，如果用户让你用 aardio 写个游戏，则你需要有步骤、有规划地完成任务，例如：

1. 先规化与设计游戏
2. 技术可行性分析，寻找合适的方法。如有需要查文档。
3. 确定要使用技术方案以后，对主要使用的技术、关键性的技术难点与可能的坑进行思考。
举例如果这个游戏适合自绘可能要用到 plus 控件与 gdip （GDI+ ) 库实现，
而自绘最关键的就是 gdip.graphics 库与 gdip.path 这几个库的运用，可以调用工具 lookup_library 看一下这两个库的文档有利于编写出更好的游戏效果。
如果是不适合 GDI+ 自绘的游戏，例如非常复杂的适合网页开发小游戏，这时候可以使用 web.view 做，如有必要可以调用调用工具 lookup_library 查看一下 web.view 的库文档。
4. 在编写代码之前，如果一些单元代码需要验证，可以调用 loadcode 或 loadcodex 先行验证。
5. 除了调用 lookup_library 查看必要的库文档，你也可以根据需要调用工具 search_example 搜索 aardio 开发环境自带的本地 aardio 代码范例。
6. 最后尝试编写代码 » 调用工具验证 » 继续改进代码 » 继续验证 ..... 直到得出满意的结果。

你可以根据最佳实践调整以上步骤与方案，要多进行权衡取舍，总之尽最大努力生成优质程序与满意的效果。你不必节省 tokens 。

## 背景

- 当前时间为：" + tostring(..time())
	++ '\r\n- 当前操作系统为："' + ..win.version.format() + '"'
	++ '\r\n- 当前农历：' +  tostring(time.lunar()) 
	++ '\r\n- aardio 开发环境所在目录：' +  io.fullpath("~/")
	++ '\r\n- 系统临时目录：' +  fsys.getTempDir()
	++ '\r\n- 可安全写入用户数据的 AppData 目录：' +  io.appData("aardio/std/example/AI/agent");

	chatUi.system(sys);
}
initSystemPrompt();

var sendApiMessage = function(){
	
	var aiConfig = config.itemData[[config.selItem]];
	 
	if(!aiConfig[["url"]] || string.find(aiConfig[["url"]],"aardio.com") ){
		winform.msgboxErr('请在设置中指定支持交错思考+工具调用的大模型。\n建议使用 Gemini Flash 3。\n\n请不要使用 ai.aardio.com 接口。');
		winform.btnSetting.oncommand();
		return;
	}
	
    var prompt = winform.editPrompt.text;

    // 检查输入是否为空（移除首尾空白）
    if(!#string.trim(prompt)){
        winform.editPrompt.setFocus();
        winform.editPrompt.showWarningTip("请输入提示词");
        return;
    }

    // UI 状态变更
    winform.editPrompt.text = ""; // 清空输入
    winform.btnSend.disabledText = ['\uF254','\uF251','\uF252','\uF253','\uF250']; 
    
    chatUi.showLoading(" 正在思考...");
    chatUi.prompt(prompt);

    // 创建 AI 对话线程
    thread.invoke( 
        function(chatUi,winform,aiConfig){
            import ide;
            import ide.doc.libraryMd;
            import ide.aifix;
            import winex;
            import web.rest.aiChat;
            
			aiConfig.temperature = 1;
      
     
            aiConfig.tools = { 
                {
            		"type": "function",
            		"function": {
                		"name": "loadcodex_async",
                		"description": "在当前电脑创建新的线路异步执行 aardio 代码。代码将作为匿名函数执行，不等待函数执行完成。函数返回值只会告知是否成功启动代码。如果运行耗时较长的代码但不需要等待程序运行完成请调用工具 loadcodex_asyn ，如果你的目的是运行耗时较短（例如只是读取一个网页，抓取一个 HTML）或者需要获得返回值，请改用可以等待返回值的 loadcodex 工具。",
                		"parameters": {
                    		"type": "object",
                    		"properties": {
                        		"code": {
                            		"type": "string",
                            		"description": "要执行的 aardio 源代码。"
                        		},
                    		},
                    		"required": {
                        		"code"
                    		}
                		}
            		}
        		},
        		{
            		"type": "function",
            		"function": {
                		"name": "loadcodex",
                		"description": "在当前电脑执行 aardio 代码。代码将作为匿名函数执行，你要通过 return 语句返回你需要的值。你要避免使用 try 语句。如果你仅仅用 `console.log()` 输出到控制台那么在工具的返回值里看不到结果（只能显示在屏幕上，除非你读取控制台输出缓冲区）。如果你使用 `console.pause()` 就会暂停并等待用户按键（不要这么做！我们需要自动安成任务）。除非你的意图就是需要用户确认，这时候你可以使用 `win.msgboxTest(text,title)` 或者 `console.askYesNo(text)` 询问用户并确认信息（你仍然要用 return 语句返回用户选择的结果）。你可以重复调用工具 loadcodex 来收集数据或者验证 aardio 代码的可用性。或者用 loadcodex 执行代码，将结果展示给用户，或者提供可操作的界面。",
                		"parameters": {
                    		"type": "object",
                    		"properties": {
                        		"code": {
                            		"type": "string",
                            		"description": "要执行的 aardio 源代码。"
                        		},
                    		},
                    		"required": {
                        		"code"
                    		}
                		}
            		}
        		},
        		{
            		"type": "function",
            		"function": {
                		"name": "loadcode",
                		"description": "仅编译 aardio 代码，主要用于检测代码是否存在语法错误。此工具不会运行代码",
                		"parameters": {
                    		"type": "object",
                    		"properties": {
                        		"code": {
                            		"type": "string",
                            		"description": "需要检测语法错误的 aardio 源代码。"
                        		},
                    		},
                    		"required": {
                        		"code"
                    		}
                		}
            		}
        		},
        		{
            		"type": "function",
            		"function": {
                		"name": "aifix",
                		"description": "调用 aardio 的 ide.aifix 自动修复 aardio 代码中的常见错误（主要用于修复 AI 生成代码的常见问题）。",
                		"parameters": {
                    		"type": "object",
                    		"properties": {
                        		"code": {
                            		"type": "string",
                            		"description": "需要检测并修正的 aardio 源代码。"
                        		},
                    		},
                    		"required": {
                        		"code"
                    		}
                		}
            		}
        		},
        		{
            		"type": "function",
            		"function": {
                		"name": "lookup_library",
                		"description": "直接获取指定 aardio 库的文档（Markdown 格式）。",
                		"parameters": {
                    		"type": "object", 
                    		"properties": {
                        		"query": {
                            		"type": "string",
                            		"description": "指定 aardio 库（内置库、标准库、扩展库等）路径，例如 'table','string' 或 'fsys.file'"
                        		}, 
                    		},
                    		"required": {
                        		"query"
                    		}
                		}
            		}
        		},
        		{
            		"type": "function",
            		"function": {
                		"name": "http_get",
                		"description": "读取给定网址的数据（通常是 HTML）。",
                		"parameters": {
                    		"type": "object", 
                    		"properties": {
                        		"url": {
                            		"type": "string",
                            		"description": "获取数据的网址链接"
                        		}, 
                    		},
                    		"required": {
                        		"url"
                    		}
                		}
            		}
        		},
        		{
            		"type": "function",
            		"function": {
                		"name": "query_aardio_knowledge",
                		"description": "专用于搜索 aardio 全部文档或范例",
                		"parameters": {
                    		"type": "object", 
                    		"properties": {
                        		"query": {
                            		"type": "string",
                            		"description": "用于搜索 aardio 文档与范例的查询字符串"
                        		}, 
                    		},
                    		"required": {
                        		"query"
                    		}
                		}
            		}
        		},
        		{
            		"type": "function",
            		"function": {
                		"name": "search_web",
                		"description": "联网搜索信息（不限于 aardio 有关）。",
                		"parameters": {
                    		"type": "object", 
                    		"properties": {
                        		"query": {
                            		"type": "string",
                            		"description": "用于搜索互联网内容"
                        		}, 
                    		},
                    		"required": {
                        		"query"
                    		}
                		}
            		}
        		},
        		{
            		"type": "function",
            		"function": {
                		"name": "ide_new_docuemnt",
                		"description": "在编辑器中打开 aardio 代码。",
                		"parameters": {
                    		"type": "object", 
                    		"properties": {
                        		"code": {
                            		"type": "string",
                            		"description": "需要在开发环境中打开的 aardio 代码（是代码，不是文件路径）"
                        		}, 
                    		},
                    		"required": {
                        		"code"
                    		}
                		}
            		}
        		},
        		{
            		"type": "function",
            		"function": {
                		"name": "ide_open_docuemnt",
                		"description": "在编辑器中打开 aardio 代码文件或工程文件。",
                		"parameters": {
                    		"type": "object", 
                    		"properties": {
                        		"path": {
                            		"type": "string",
                            		"description": "*.aardio 或 *.aproj 文件路径"
                        		}, 
                    		},
                    		"required": {
                        		"path"
                    		}
                		}
            		}
        		},
        		{
            		"type": "function",
            		"function": {
                		"name": "search_example",
                		"description": "在开发环境的本地范例目录搜索 aardio 范例。",
                		"parameters": {
                    		"type": "object", 
                    		"properties": {
                        		"pattern": {
                            		"type": "string",
                            		"description": "使用 aardio 模式匹配语法指定搜索模式串"
                        		}, 
                    		},
                    		"required": {
                        		"pattern"
                    		}
                		}
            		}
        		},
        		{
            		"type": "function",
            		"function": {
                		"name": "save_string",
                		"description": "保存文件",
                		"parameters": {
                    		"type": "object", 
                    		"properties": {
                        		"path": {
                            		"type": "string",
                            		"description": "写入的文件路径"
                        		}, 
                    		},
                    		"required": {
                        		"path"
                    		}
                		}
            		}
        		},
        		{
            		"type": "function",
            		"function": {
                		"name": "load_string",
                		"description": "读取文件",
                		"parameters": {
                    		"type": "object", 
                    		"properties": {
                        		"path": {
                            		"type": "string",
                            		"description": "要读取的文件路径。"
                        		}, 
                    		},
                    		"required": {
                        		"path"
                    		}
                		}
            		}
        		} 
    		}
            
            // 创建 AI API 客户端
            var aiClient = web.rest.aiChat(aiConfig)
            
            var getArgs1 = function(args,name){
            	var arg1 = args[[name]];
				if(arg1) return arg1;
				
				for(k,v in args){
					if(type(v)==="string") return v;
				}
				
				import console
				console.dump(args)
				console.error("调用工具时缺少参数：",name)
            }
            
            
            aiClient.external = {
            	loadcodex_async = function(args){ 
            	    var code = getArgs1(args,"code");
            	    if(!code) return "调用此工具必须指定 `code` 参数"
			
					chatUi.showThinking('\n\n```aardio\n'+code+'\n```\n\n');
				 
					var func,err = loadcode(code)
					if(!func){
						return  {
							error = err;
							comment = "加载当前代码时遇到了错误，error 字段为错误信息。"
						}	
					}
					
					thread.invoke( 
						function(code){
							import win;
							import winex;
							import JSON;
							winex.attachThread(thread.getMainId())
							
							loadcodex(code);
						},code
					);	
            		winex.attachThread(thread.getMainId())
            			    
					  
					aiClient.alreadyExecuteCode = true;
					
					//让主线程的 AI 消息队列记住最后执行成功的代码块，要考虑一轮对话结束思维链会弃
					chatUi.system('已调用 loadcodex_async 工具成功启动以下代码：\n\n```aardio'++code+'\n\n```')
		 			
					return  {
						result = true; 
						comment = "编译代码时未发生错误，已成功启动程序。"
					}
				},
				loadcodex = function(args){ 
				
					var code = getArgs1(args,"code");
            	    if(!code) return "调用此工具必须指定 `code` 参数"
					
					chatUi.showThinking('\n\n```aardio\n'+code+'\n```\n\n');
				 
					var func,err = loadcode(code)
					if(!func){
						return  {
							error = err;
							comment = "加载当前代码时遇到了错误，error 字段为错误信息。"
						}	
					}
					
            		winex.attachThread(thread.getMainId())
            			    
					var ok,result =  call(func); 
					if(!ok){
						return  {
							error = result;
							comment = "运行当前代码时遇到了错误，error 字段为错误信息。"
						}
					}
					
					aiClient.alreadyExecuteCode = true;
					
					//让主线程的 AI 消息队列记住最后执行成功的代码块，要考虑一轮对话结束思维链会弃
					chatUi.system('已调用 loadcodex 工具成功执行以下代码：\n\n```aardio'++code+'\n\n```')
		 			
					return  {
						result = result; 
						comment = "代码已成功执行，未发生错误。result 字段为执行代码的返回值。"
					}
				},
				loadcode = function(args){ 
					var code = getArgs1(args,"code");
            	    if(!code) return "调用此工具必须指定 `code` 参数"
					
					var func,err = loadcode(code)
					if(!func){
						return  {
							error = err;
							comment = "编译当前代码时遇到了错误，error 字段为错误信息。"
						}	
					}
		 			
					return  {
						result = "OK"; 
						comment = "代码编译成功，无语法错误错误。"
					}
				}, 
				aifix = function(args){ 
					var code = getArgs1(args,"code");
            	    if(!code) return "调用此工具必须指定 `code` 参数"
					
					var code = string.match(code,"!\N([ \t]*)(```+)<aardio>(.+?)!\N\s*\2![^`\S]") || code;
					
					var newCode = ide.aifix(code)
					if(newCode){
						return  {
							code = '```aardio\n'+newCode+'\n```';
							comment = "code 字段包含的 Markdown 代码块内部是修复后的代码。"
						}	
					}
				},
				ide_new_docuemnt = function(args){ 
					var code = getArgs1(args,"code");
            	    if(!code) return "调用此工具必须指定 `code` 参数"
				
					import ide; 
					ide.openCode(code)
						
					return  {
						comment = "已调用 aardio 接口 ide.openCode 在开发环境的代码编辑器中打开输入的代码。"
					}	
				},
				ide_open_docuemnt = function(args){
					var path = getArgs1(args,"path");
            	    if(!path) return "调用此工具必须指定 `path` 参数"
            	    
            	    if(!io.exist(path)){
            	    	return { error = " path 参数指定了错误的文件路径"}
            	    }
            	    
            	    if(!..string.match(path,".<aardio>|<aproj>$")){
            	    	return { error = " path 参数只指定代码文件（ *.aardo ）或工程文件（ *.aproj ）文件路径。"}
            	    }
				
					import ide; 
					ide.openDocument(path)
						
					return  {
						comment = "已调用 aardio 接口 ide.openDocument 在开发环境中打开指定的文件。"
					}	
				},
				save_string = function(args){  
					var path = getArgs1(args,"path");
            	    if(!path) return "调用此工具必须指定 `path` 参数"
            	    
            	    var data = args.data;
            	    if(!data) return "调用此工具必须指定 `data` 参数"
				
					var ok,err = ..string.save(path,data)
					if(..string.save(path, data)){
						return  {
							comment = "已成功保存文件。"
						}	
					}
						
					return  {
						error = err;
						comment = "保存文件时遇到错误。"
					}		
				},
				load_string = function(args){ 
					var path = getArgs1(args,"path");
            	    if(!path) return "调用此工具必须指定 `path` 参数"
            	    
					var data = ..string.load(path)
					if(data){
						return  {
							data = data;
							comment = "已成功读取文件，文件数据请查看 data 字段值。"
						}	
					}
						
					return  {
						comment = "读取文件失败。"
					}		
				},
				search_example = function(args){
					var pattern = getArgs1(args,"pattern");
            	    if(!pattern) return "调用此工具必须指定 `pattern` 参数"
            	     
            	  	var result = []
            	    fsys.enum( "~/example","*.aardio",
            	    	function(dir,filename,fullpath,findData){ 
            	    		if(filename){ 
            	    		    
            	               var code = string.load(fullpath)
            	               if(string.find(code,pattern)){ 
            	               		..table.push(result,fullpath);
            	               }
            	    		}
            	    	} 
            	    );
            	    
            	    if(#result){
            	    	return {
            	    		files = result;
            	    		comment = "files 字段包含的数组是找到的全部代码文件，这些文件包含的内容符合指定的模式匹配表达式参数，你应当进一步判定与筛选，然后根据需要调用 load_string 读取文件内容。"
            	    	}
            	    }
				},
				lookup_library = function(args){ 
					var query = getArgs1(args,"query");
            	    if(!query) return "调用此工具必须指定 `query` 参数"
            	    
					var md = ide.doc.libraryMd(query)
					
					if(!md){
						return  {
							error = "错误的 lib 参数（库路径不存在或者用户未安装指定的扩展库）";
						}	
					}
		 			
					return  {
						result = md;
						comment = "result 字段为找到的库文档（Markdown 格式）"
					}
				},
				http_get = function(args){ 
					var url = getArgs1(args,"url");
            	    if(!url) return "调用此工具必须指定 `url` 参数"
            	  
					var data,err = inet.http.get(url)
					
					if(!data){
						return  {
							error = err || "未知错误";
						}	
					}
		 			
					return  {
						result = data;
						comment = "result 字段为下载的数据"
					}
				},
				query_aardio_knowledge = function(args){
					var query = getArgs1(args,"query");
            	    if(!query) return "调用此工具必须指定 `query` 参数"
					
					chatUi.showThinking('\n搜索：'+query+'\n\n\n');
					
					import string.escape2;
					import web.rest.jsonClient;
					var http = web.rest.jsonClient();
					
					//请将 Key 换成自己的（注册 tavily 就可以领，每月免费 1000 次）。 
					http.setAuthToken(string.escape2('\0\112\4\6\28\9\93\20\21\6\93\54\62\66\72\57\31\5\3\27\64\9\8\73\49\50\52\57\61\49\56\61\69\51\68\9\56\38\61\39\37\31\26'));
					var tavily = http.api("https://api.tavily.com");
					
					//搜索，不建议指定 include_raw_content 参数（ 返回的 raw_content 可能有乱码 ）.
					var resp = tavily.search(
    					query = query,
    					max_results = 5, //限制返回结果数，默认值为 5。 
    					include_domains = ["www.aardio.com"], //可选用这个字段限定搜索的域名
					)
					
					//利用消息对象自动合成提示词
					import web.rest.aiChat; 
					var msg = web.rest.aiChat.message(); 
					msg.url(resp[["results"]]) //将搜索结果添加到系统提示词
					
					if(#msg){
						return msg[#msg].content;//返回合成的提示词
					}
		
				} ,
				search_web = function(args){
					var query = getArgs1(args,"query");
            	    if(!query) return "调用此工具必须指定 `query` 参数"
					
					chatUi.showThinking('\n搜索：'+query+'\n\n\n');
					
					import string.escape2;
					import web.rest.jsonClient;
					var http = web.rest.jsonClient();
					
					//请将 Key 换成自己的（注册 tavily 就可以领，每月免费 1000 次）。 
					http.setAuthToken(string.escape2('\0\112\4\6\28\9\93\20\21\6\93\54\62\66\72\57\31\5\3\27\64\9\8\73\49\50\52\57\61\49\56\61\69\51\68\9\56\38\61\39\37\31\26'));
					var tavily = http.api("https://api.tavily.com");
					
					//搜索，不建议指定 include_raw_content 参数（ 返回的 raw_content 可能有乱码 ）.
					var resp = tavily.search(
    					query = query,
    					max_results = 5, //限制返回结果数，默认值为 5。 
					)
					
					//利用消息对象自动合成提示词
					import web.rest.aiChat; 
					var msg = web.rest.aiChat.message(); 
					msg.url(resp[["results"]]) //将搜索结果添加到系统提示词
					
					if(#msg){
						return msg[#msg].content;//返回合成的提示词
					}
		
				} 
			}

            // 调用接口
            var chatMessage = chatUi.chatMessage;
            var ok,err,errCode = aiClient.messages(chatMessage,
                function(deltaText,reasoning){
                    if(reasoning) chatUi.showThinking(reasoning);
                    chatUi.assistant(deltaText);
                }
            );
            
            if(err){ 
                if(errCode==12029){
                	chatUi.errorMessage(`旧版操作系统不支持此服务商接口！<a href="javascript:void(0)" onclick="javascript:external.updateApiKey()">点这里更换其他接口</a>`)
                }
                elseif(aiClient.lastStatusCode==401){
                	chatUi.errorMessage(`API 密钥错误！<a href="https://imtip.aardio.com/api-keys/">点这里领取 tokens</a>，&nbsp;<a href="javascript:void(0)" onclick="javascript:external.updateApiKey()">点这里设置新密钥</a>`)
                }
                else{
                	 chatUi.errorMessage(err)
                } 
            }
            else{
            	var md = chatUi.lastAssistantMessage(true);
            	if(!aiClient.alreadyExecuteCode){
            
            		/*
            		var indent,_,code = string.match(md,"!\N([ \t]*)(<```+>|<~~~+>)<aardio>(.+?)!\N\s*\2![^`\S]")
				
				
            		if(code){
            			var f,err1 = loadcode(code);
            			if(!f) {
            				code = ide.aifix(code)
            				f = loadcode(code); 
            			}
            			
            			if(f) {
            			    ..winex.attachThread(thread.getMainId())
            				f();
            			}
            			elseif(type(err1)==="string"){
            			    winform.btnSend.disabled = false;
            				chatUi.prompt('编译你给的代码时出现错误：\n\n```txt\n' +err1+'\n```\n');
            				winform.btnSend.oncommand();
            				return;
            			}
            		}
            		*/
            	}
            }
            
            // 恢复按钮状态
            winform.btnSend.disabledText = null;
        },chatUi,winform,aiConfig
    )
    
    // 发送后焦点切回输入框
    winform.editPrompt.setFocus();
}

winform.editPrompt.text = "获取我的公网 IP，然后打开 必应 搜索此 IP"

chatUi.external = {
	updateApiKey = function(){
		winform.btnSetting.oncommand();
	} 
} 

winform.btnSend.oncommand = function(id,event){
    sendApiMessage();
}

//回车发送（Ctrl+Enter 换行）
winform.editPrompt.onOk = function(ctrl,alt,shift){
    if(ctrl){
        winform.btnSend.oncommand();
        return true;
    }
}

winform.btnSend.skin({
    background={
        default=0xFF0078D7;
        hover=0xFF0088EE;
        active=0xFF005BB5;
        disabled=0xFFCCCCCC;
    };
    color={
        default=0xFFFFFFFF;
        disabled=0xFF888888;
    }
})

winform.btnSetting.skin({
	color={
		active=0xFF00FF00;
		default=0xFF3C3C3C;
		disabled=0xFF6D6D6D;
		hover=0xFFFF0000		
	}
})

winform.show();
win.loopMessage();
``````