基于Koishi框架的QQ机器人开发:从事件驱动到插件化实战

📅 2026/7/31 3:26:55
基于Koishi框架的QQ机器人开发:从事件驱动到插件化实战
1. 项目概述为什么选择Koishi来打造你的第一个QQ机器人最近几年QQ机器人这个领域可以说是越来越热闹了。从早些年需要自己搭建服务器、写复杂代码的“硬核”玩法到现在各种框架和平台让搭建过程变得像搭积木一样简单门槛是实实在在地降低了。如果你也想拥有一个能自动回复、管理群聊、甚至陪你聊天的QQ机器人但又不想从零开始啃那些晦涩的协议文档那么Koishi绝对是一个值得你优先考虑的“瑞士军刀”。简单来说Koishi是一个开源的、跨平台的机器人框架。它最大的魅力在于“开箱即用”和“生态丰富”。你不用关心QQ的底层通信协议是怎么握手、怎么收发消息的Koishi已经帮你把这些脏活累活都封装好了。你只需要像安装手机App一样安装你需要的功能插件然后进行简单的配置一个功能各异的机器人就诞生了。无论是想做一个自动回复关键词的“复读机”一个能查询天气、翻译句子的“工具人”还是一个能管理群成员、定时发送通知的“小管家”Koishi的插件市场里几乎都能找到对应的轮子。我选择用它来实现“简易QQ机器人”核心原因就是它的“简易”是真正面向开发者和爱好者的简易而不是功能上的简陋。它提供了强大的控制台、清晰的事件响应机制和活跃的社区让你在实现基础功能的同时还保留了巨大的自定义和扩展空间。这意味着你的机器人可以从“简易”开始随着你的想法增多逐步成长为一个“复杂”的智能体。2. 核心思路与架构拆解Koishi是如何工作的在动手之前我们得先搞明白Koishi这套框架的基本工作逻辑。这样后面配置和写插件时你才能知道每一步在干什么出了问题也知道该往哪个方向排查。2.1 事件驱动模型机器人的“神经系统”Koishi的核心是一个事件驱动的模型。你可以把整个机器人看作一个一直在监听各种事件的“耳朵”。当特定事件发生时比如有人发送了群消息、有人申请加好友、机器人自己被邀请入群等Koishi就会产生一个对应的事件对象然后把这个对象抛给所有注册了对这个事件感兴趣的“处理器”去处理。举个例子用户张三在群里说了一句“今天天气怎么样”。这个动作在Koishi内部会触发一个message事件。你的代码里可能写了一个处理器专门监听所有群消息message事件并且判断消息内容是否包含“天气”关键词。如果包含这个处理器就会被激活执行查询天气的代码并将结果发送回群里。这种模式的优点非常明显解耦和灵活。发送消息、接收消息、处理消息的逻辑是分离的。你可以轻松地为同一个事件添加多个处理器也可以随时启用或禁用某个处理器而不会影响其他功能。这就像给你的机器人装上了模块化的“神经系统”哪里需要功能就在哪里接上一个“神经单元”。2.2 插件化生态功能即插即用Koishi的第二个核心设计是插件化。在Koishi的世界里几乎一切功能都是一个插件。机器人连接QQ需要一个官方提供的适配器插件比如adapter-onebot用于连接主流协议实现处理消息、实现功能需要你自己编写或安装的功能插件甚至管理后台本身也是一个插件。这种设计带来了巨大的便利功能复用你不用重复造轮子。需要机器人群管功能去插件市场搜一个现成的群管插件安装即可。独立开发与维护每个插件都是独立的有自己的配置、自己的生命周期。更新或卸载一个插件不会影响其他插件。依赖管理清晰Koishi会自动处理插件之间的依赖关系。比如你安装的“游戏”插件依赖“数据库”插件Koishi会确保先加载数据库插件。对于我们的简易机器人我们主要会用到两类插件一个官方适配器插件用于让Koishi能和QQ通信以及一个或多个我们自己写的简单功能插件。2.3 配置即代码一切皆可配置Koishi推崇“配置即代码”的理念。你的机器人的所有设置包括启用哪些插件、每个插件的参数、数据库连接信息等都可以通过一个配置文件通常是koishi.yml或直接在代码中定义。这种方式让部署和迁移变得非常容易你只需要备份这个配置文件就能在另一台服务器上快速复现一个一模一样的机器人。3. 环境准备与项目初始化从零搭建基础理论说得差不多了我们开始动手。第一步是把Koishi框架和必要的环境搭建起来。3.1 环境准备Node.js是基石Koishi基于Node.js运行所以第一步是确保你的电脑或服务器上安装了Node.js环境。建议安装最新的LTS长期支持版本稳定性更有保障。打开终端命令行输入以下命令检查是否安装成功node -v npm -v如果正确显示版本号如v18.x.x和9.x.x说明环境已经就绪。如果没有请去Node.js官网下载安装。注意不建议使用系统自带的、版本过旧的Node.js可能会遇到奇怪的兼容性问题。使用nvmNode Version Manager工具来管理多个Node.js版本是一个好习惯可以灵活切换。3.2 创建项目与安装Koishi接下来我们创建一个全新的目录作为机器人的项目文件夹并初始化一个Koishi项目。# 创建一个名为 my-qq-bot 的文件夹并进入 mkdir my-qq-bot cd my-qq-bot # 使用 npm 初始化项目一路按回车使用默认值即可 npm init -y # 安装 Koishi 核心包和命令行工具 npm install koishi koishijs/cli -D这里我们安装了koishi核心框架包以及koishijs/cli这个命令行工具。CLI工具能极大简化后续的插件管理、项目启动等操作。3.3 创建配置文件与安装适配器Koishi需要一个配置文件来指导它如何运行。最简单的方式是使用CLI工具来生成一个基础配置模板。# 使用 Koishi CLI 初始化配置文件 npx koishi init执行这个命令后它会引导你进行一些基础选择比如配置文件的格式推荐YAML更简洁并最终在项目根目录生成一个koishi.yml文件。同时它也会帮你安装最基础的官方插件比如控制台插件。现在我们需要安装连接QQ所必需的适配器插件。目前最主流的是基于OneBot协议的实现。OneBot是一个开源、统一的机器人应用接口标准有很多实现如go-cqhttp、LLOneBot等遵循这个标准。Koishi通过adapter-onebot插件与这些实现通信。# 安装 OneBot 适配器插件 npm install koishijs/plugin-adapter-onebot安装完成后我们需要修改koishi.yml配置文件告诉Koishi启用这个适配器并配置连接信息。打开koishi.yml文件你会看到类似下面的结构# koishi.yml plugins: # 这里列出了所有启用的插件及其配置我们需要在其中添加adapter-onebot的配置。假设我们使用一个已经在运行的、支持OneBot v11协议的客户端例如go-cqhttp它通过WebSocket在ws://localhost:6700监听。那么配置如下plugins: # 其他插件... adapter-onebot: protocol: ws endpoint: ws://localhost:6700 selfId: 123456789 # 这里替换为你机器人的QQ号protocol: 通信协议常用ws(WebSocket) 或http。endpoint: 你的OneBot客户端如go-cqhttp提供的服务地址。selfId: 机器人的QQ号用于标识自身。实操心得selfId这个配置非常重要Koishi内部很多逻辑比如判断消息是不是自己发的都依赖它。务必确保这里填写的QQ号和实际登录的机器人QQ号一致否则可能导致消息循环发送等诡异问题。4. 编写第一个功能插件让机器人“开口说话”框架搭好了QQ也连上了但现在机器人还是个“哑巴”。我们来编写第一个最简单的功能插件让机器人在收到特定指令时回复一句话。4.1 插件的基本结构在Koishi中一个插件可以是一个对象也可以是一个函数返回一个对象。我们创建一个新文件src/plugins/echo.js或.ts如果你用TypeScript。// src/plugins/echo.js module.exports (ctx) { // ctx 是插件上下文提供了访问 Koishi 核心 API 的能力 // 监听所有消息事件 ctx.on(message, (session) { // session 对象包含了这次消息事件的所有信息发送者、群号、消息内容等 // 判断消息内容是否是指令 “.echo 你好” if (session.content .echo 你好) { // 使用 session.send() 方法回复消息 return session.send(你好我是Koishi机器人。) } }) }这个插件做了以下几件事通过ctx.on(message, callback)监听所有消息事件。在回调函数中通过session.content获取消息的纯文本内容。判断内容是否完全等于.echo 你好。如果是则调用session.send()方法向收到消息的同一个上下文私聊或群聊发送回复。4.2 注册并启用插件编写完插件代码后我们需要在配置文件中启用它。修改koishi.ymlplugins: adapter-onebot: protocol: ws endpoint: ws://localhost:6700 selfId: 123456789 # 添加你的 echo 插件./src/plugins/echo 是插件文件的路径相对于项目根目录 ./src/plugins/echo:Koishi会自动加载指定路径下的插件。现在启动你的Koishi项目npx koishi start如果一切正常控制台会输出启动日志显示插件加载成功。此时在你的QQ上对机器人发送.echo 你好它就应该会回复“你好我是Koishi机器人。”了。4.3 使用指令系统更优雅的交互上面我们直接匹配了完整的消息字符串这在实际应用中很不灵活。Koishi提供了一套强大的指令系统可以更方便地解析用户输入处理参数。让我们用指令系统重写上面的功能。创建一个新插件src/plugins/greet.js// src/plugins/greet.js module.exports (ctx) { // 使用 ctx.command() 定义一个指令 ctx.command(greet name:text, 向某人打招呼) // .alias(你好) // 可以为指令设置别名用户输入“你好”也能触发 .action(({ session }, name) { // 这个 action 回调会在指令被触发时执行 // name 参数对应指令定义中的 name:text return 你好呀${name}欢迎使用Koishi。 }) }这个插件定义了一个名为greet的指令它接受一个必填的参数name参数类型是文本。当用户在聊天中发送greet 张三或.greet 张三默认指令前缀是点号时机器人就会回复“你好呀张三欢迎使用Koishi。”指令系统的优势自动参数解析不用自己拆分字符串Koishi帮你把greet 张三 李四这样的参数自动解析好。类型检查可以定义参数类型text, number, user等无效输入会被自动拦截并提示用户。帮助系统自动生成指令使用说明用户发送help或greet -h就能查看。权限管理可以轻松为指令设置使用权限如仅管理员可用。注意事项指令的默认前缀通常是点号.但这可以在Koishi的全局配置中修改。有些机器人实现或协议端可能会过滤掉某些特殊符号开头的消息如果发现指令不触发可以检查一下协议端的相关配置。5. 实现实用功能天气查询与群管有了指令系统的基础我们就可以实现一些更实用的功能了。这里以两个常见需求为例天气查询和简易群管。5.1 调用外部API实现天气查询机器人自己不知道天气我们需要让它学会“上网查”。这里以调用一个免费的天气API为例例如和风天气、OpenWeatherMap等需要自行申请API Key。创建插件src/plugins/weather.js// src/plugins/weather.js const axios require(axios) // 需要先安装 axios: npm install axios module.exports (ctx) { ctx.command(weather city:text, 查询城市天气) .example(weather 北京) // 提供使用示例 .action(async ({ session }, city) { // 注意action 回调可以是 async 函数以便进行网络请求 const apiKey 你的API密钥 // 请替换为真实的API Key const apiUrl https://api.someweather.com/v3/weather/now?key${apiKey}location${encodeURIComponent(city)} try { const response await axios.get(apiUrl) const data response.data if (data.code 200) { const weather data.now return ${city}的天气情况${weather.text}温度${weather.temp}℃湿度${weather.humidity}%风向${weather.windDir}风力${weather.windScale}级。 } else { return 查询失败${data.message || 未知错误} } } catch (error) { // 网络请求出错 ctx.logger(weather).error(天气API请求失败, error) return 天气查询服务暂时不可用请稍后再试。 } }) }关键点解析异步操作网络请求是异步的所以action回调函数标记为async并使用await等待请求结果。错误处理使用try...catch包裹网络请求确保即使API出错机器人也不会崩溃而是能给用户一个友好的提示。日志记录使用ctx.logger(weather)创建了一个名为“weather”的日志器将错误信息记录下来方便后期排查问题而不是直接把错误堆栈返回给用户。安全提示API密钥是敏感信息绝对不要硬编码在代码中提交到公开的代码仓库。应该使用环境变量或Koishi的配置管理功能。在koishi.yml中配置会更安全plugins: ./src/plugins/weather: apiKey: ${WEATHER_API_KEY} # 从环境变量读取然后在插件中通过ctx.config.apiKey获取。5.2 简易群管功能禁言与欢迎群管理是机器人的常见用途。利用Koishi提供的会话API我们可以轻松实现一些基础管理功能。创建插件src/plugins/group-manager.js// src/plugins/group-manager.js module.exports (ctx) { // 1. 禁言指令通常需要管理员权限 ctx.command(mute user:user duration:number, 禁言指定用户) .option(reason, -r reason:text 禁言理由) // 添加选项 .alias(禁言) .action(async ({ session, options }, user, duration) { // 检查发送者是否有管理员权限这里简单判断实际应用应更严谨 if (!session.event.member.isAdmin) { return 抱歉此命令需要管理员权限。 } const userId user.replace(qq:, ) // 从 user 参数中提取QQ号 const reason options.reason ? 理由${options.reason} : try { // 调用 OneBot 协议的禁言API await session.bot.internal.setGroupBan(session.event.groupId, userId, duration * 60) // 分钟转秒 return 已禁言用户 ${userId} ${duration} 分钟${reason}。 } catch (error) { ctx.logger(group-manager).error(禁言失败, error) return 禁言操作失败请检查权限或用户状态。 } }) // 2. 新人入群欢迎 ctx.on(group-member/increase, (session) { // 当有群成员增加时触发此事件 // session.event.userId 是新人的QQ号 // session.event.groupId 是群号 // 可以设置一个欢迎语模板 const welcomeMsg 欢迎新朋友 [CQ:at,qq${session.event.userId}] 加入本群\n请阅读群公告遵守群规哦~ // 延迟一秒发送避免刷屏 setTimeout(() { session.send(welcomeMsg).catch(e ctx.logger(welcome).error(e)) }, 1000) }) // 3. 关键词撤回简易风控 const forbiddenWords [广告链接, 违禁词1, 违禁词2] ctx.on(message, (session) { if (session.event.subType ! normal) return // 忽略自身消息或其他特殊消息 if (session.event.messageType ! group) return // 只处理群消息 const message session.content for (const word of forbiddenWords) { if (message.includes(word)) { // 尝试撤回消息 session.bot.deleteMsg(session.event.messageId).catch(e {}) // 警告发送者 session.send([CQ:at,qq${session.event.userId}] 请注意言行请勿发送违规内容。) break // 找到一个违禁词就处理跳出循环 } } }) }功能解析与注意事项权限判断管理指令如禁言必须进行严格的权限校验。示例中只是简单判断在生产环境中应使用Koishi内置的权限系统ctx.permission()或结合平台的角色信息进行更精确的判断。API调用session.bot.internal提供了访问底层OneBot协议API的能力。不同协议端的API可能略有差异需要查阅对应文档。setGroupBan是标准OneBot v11 API。事件处理group-member/increase是Koishi标准化的事件名对应群成员增加。使用事件监听可以实现自动化的响应。消息撤回与风控关键词过滤是简单的风控手段。注意撤回消息的API可能需要机器人在群内拥有管理员权限。此外过于敏感的词库和频繁的撤回操作可能影响群内体验需谨慎设置。CQ码在发送的消息字符串中[CQ:at,qq123456]是一种特殊的“CQ码”用于指定用户。这是OneBot协议定义的消息段格式Koishi的适配器会将其转换为QQ客户端能识别的格式。6. 插件配置与管理让机器人更智能随着插件增多管理配置和插件行为就变得重要了。Koishi提供了强大的配置管理和插件市场支持。6.1 为插件添加可配置项让我们改进之前的天气插件让API Key和API地址可以通过配置文件管理而不是硬编码。首先修改src/plugins/weather.js使用Koishi的Schema来定义配置// src/plugins/weather.js const axios require(axios) // 使用 exports.schema 定义插件配置的结构 exports.schema { apiKey: { type: string, required: true, desc: 天气服务的API密钥 }, apiBase: { type: string, default: https://api.someweather.com/v3, desc: 天气API的基础地址 }, defaultCity: { type: string, default: 北京, desc: 当未提供城市参数时使用的默认城市 } } module.exports (ctx, config) { // 注意这里第二个参数 config 就是注入的配置 ctx.command(weather [city:text], 查询城市天气) // city 改为可选参数 .option(detail, -d 显示详细预报) .action(async ({ session, options }, city) { // 使用传入的 config const targetCity city || config.defaultCity const apiUrl ${config.apiBase}/weather/now?key${config.apiKey}location${encodeURIComponent(targetCity)} try { const response await axios.get(apiUrl) const data response.data // ... 后续处理逻辑与之前相同使用 config.apiKey 和 config.apiBase ... let reply ${targetCity}的天气${data.now.text}温度${data.now.temp}℃。 if (options.detail) { reply 湿度${data.now.humidity}%风力${data.now.windScale}级。 } return reply } catch (error) { ctx.logger(weather).error(error) return 查询失败。 } }) }然后在koishi.yml中配置这个插件plugins: ./src/plugins/weather: apiKey: ${WEATHER_API_KEY} # 从环境变量读取安全 apiBase: https://devapi.qweather.com/v7 defaultCity: 上海这样插件的行为就完全由配置文件控制了更换API服务商或默认城市都非常方便。6.2 使用官方与社区插件Koishi拥有一个活跃的插件市场里面有大量官方和社区贡献的插件。你可以通过控制台图形界面或命令行轻松安装。例如你想安装一个“签到”插件让群友每天可以打卡领积分# 通过CLI搜索插件 npx koishi search 签到 # 假设找到了一个叫 koishi-plugin-check-in 的插件安装它 npm install koishi-plugin-check-in # 然后在 koishi.yml 中启用它在koishi.yml中添加plugins: koishi-plugin-check-in: # 该插件自己的配置项...重启Koishi后签到功能就自动加载了。通过这种方式你可以像搭积木一样快速为你的机器人增添各种复杂功能而无需从头编写。6.3 插件热重载与日志查看Koishi的开发体验非常友好。在开发模式下npx koishi start -w或npx koishi dev修改插件代码后保存Koishi会自动重新加载该插件无需重启整个应用。这大大提升了开发调试效率。此外Koishi内置了功能强大的日志系统。你可以在控制台如果安装了控制台插件并通过浏览器访问清晰地看到机器人的所有活动事件触发、指令调用、API请求、错误信息等。通过过滤日志级别和搜索关键词可以快速定位问题。7. 部署与上线让机器人7x24小时运行在本地开发测试完成后你需要将机器人部署到一台稳定的服务器上才能实现24小时在线。7.1 准备生产环境服务器选择可以选择云服务商如腾讯云、阿里云的轻量应用服务器或是有公网IP的家用NAS/旧电脑。系统推荐Linux如Ubuntu。安装基础环境在服务器上安装Node.js、npm或yarn/pnpm和Git。克隆代码将你的机器人项目代码上传到服务器通过Git克隆或FTP上传。安装依赖在项目目录运行npm install --production--production参数只安装运行依赖不安装开发依赖减少体积。配置环境变量将API密钥等敏感信息设置为服务器的环境变量或在服务器上创建安全的配置文件。7.2 使用进程守护工具不能让机器人仅仅在终端前台运行终端一关就没了。我们需要一个进程守护工具来管理它。推荐使用 PM2# 在服务器上全局安装 PM2 npm install pm2 -g # 进入你的机器人项目目录 cd /path/to/my-qq-bot # 使用 PM2 启动 Koishi pm2 start npm --name my-qq-bot -- start # 或者如果你在 package.json 中配置了脚本例如 start: koishi start # pm2 start npm --name my-qq-bot -- run start # 设置开机自启 pm2 startup pm2 savePM2会守护你的Koishi进程如果崩溃了会自动重启还能方便地查看日志pm2 logs my-qq-bot和管理进程状态。7.3 配置反向WebSocket连接针对go-cqhttp等在本地测试时我们通常让Koishi连接本机运行的协议客户端如go-cqhttp。在服务器部署时为了安全更常见的做法是让协议客户端运行在可以登录QQ的机器上反向连接到部署在公网服务器的Koishi。修改协议端配置以go-cqhttp的config.yml为例# go-cqhttp 的配置 ... servers: - ws-reverse: universal: ws://你的公网服务器IP:端口/onebot/v11/ws # Koishi 提供的WebSocket地址 reconnect-interval: 5000 max-reconnect-times: 10在Koishi的koishi.yml中adapter-onebot配置需要调整plugins: adapter-onebot: # 不再需要 endpoint因为现在是客户端连我们 # 但需要配置 selfId selfId: 123456789 # 可选配置心跳等高级参数同时你需要确保服务器的防火墙开放了对应的端口并且Koishi正确配置了HTTP/WebSocket服务默认已配置。8. 常见问题排查与优化心得在实际搭建和运行过程中你肯定会遇到各种各样的问题。这里记录一些我踩过的坑和解决方案。8.1 消息收不到或发不出这是最常见的问题通常出现在通信链路环节。问题现象可能原因排查步骤机器人完全无响应1. 协议客户端未运行或崩溃。2. Koishi与协议客户端网络不通。3. 配置中的selfId或endpoint错误。1. 检查go-cqhttp等客户端日志确认已成功登录QQ。2. 在服务器上用curl或telnet测试endpoint地址端口是否可达。3. 核对koishi.yml中adapter-onebot的selfId是否为机器人QQ号。能收到消息但不回复1. 插件未正确加载或启用。2. 指令前缀不匹配或被过滤。3. 插件代码逻辑错误如条件判断错误。1. 查看Koishi控制台日志确认你的插件在启动时已被加载。2. 尝试发送纯文本消息非指令看插件的事件监听是否触发。3. 在插件代码中添加ctx.logger输出调试信息查看逻辑执行到哪一步。仅部分功能不正常1. 特定插件代码有Bug。2. 权限不足如管理指令。3. 外部API调用失败。1. 查看该插件相关的错误日志。2. 检查权限判断逻辑或尝试在更高权限的上下文测试。3. 检查网络连接和API密钥有效性。实操心得善用日志。Koishi的日志输出非常详细遇到问题第一反应应该是打开日志控制台或PM2日志从最新的错误信息开始向上追溯。90%的问题都能通过日志找到线索。8.2 性能与稳定性优化当你的机器人插件越来越多群聊消息量增大时可能需要考虑一些优化。数据库选择Koishi默认使用SQLite轻量但并发读写性能一般。如果机器人需要存储大量数据如用户积分、游戏状态可以考虑切换到MySQL或PostgreSQL。在配置中修改数据库连接即可。插件懒加载不是所有插件都需要在启动时就加载。对于使用频率低的功能可以研究Koishi的插件懒加载机制减少启动时间和内存占用。指令防滥用对于耗时的指令如图片生成、网络查询可以添加调用频率限制防止用户刷屏导致机器人卡顿或被风控。ctx.command(slow-cmd, 一个耗时命令) .action(async ({ session }) { // 使用 ctx.throttle 限制每个用户每60秒只能调用1次 const key slow-cmd:${session.userId} if (ctx.throttle.get(key)) { return 命令执行过于频繁请稍后再试。 } ctx.throttle.set(key, true, 60000) // 设置60秒的冷却时间 // ... 执行耗时操作 ... })错误处理与降级对于依赖外部服务的功能如天气、翻译一定要做好错误处理。网络超时、API限流等情况都要考虑给用户友好的降级提示而不是让机器人直接抛出异常崩溃。8.3 关于QQ平台风控这是一个无法回避的现实问题。腾讯对于自动化行为机器人的检测和限制越来越严格。新号风险高新注册的QQ号或低活跃度号用于登录机器人客户端极易被冻结。建议使用有一定登录历史、活跃度的“老号”。行为模式化不要让你的机器人行为过于规律或频繁。例如避免在多个群以固定时间间隔发送完全相同的内容。避免敏感操作大规模、高频次的加好友、加群、发消息尤其是包含链接、二维码等行为是风控的重点打击对象。协议客户端选择与更新使用稳定、维护积极的协议客户端如go-cqhttp并及时更新到最新版本以应对QQ客户端的协议变化。准备备用方案理解并接受机器人可能随时无法登录的风险。对于重要业务考虑使用腾讯官方提供的、更稳定的机器人接入渠道如企业QQ、QQ频道机器人虽然功能和自由度可能受限。搭建QQ机器人是一个融合了技术动手能力和对平台规则理解的过程。Koishi框架极大地降低了技术门槛让你能更专注于创意和功能的实现。从最简单的回声机器人开始逐步添加天气、管理、游戏、互动等各种插件看着自己创造的“数字生命”在社群中活跃起来这种成就感是独一无二的。记住从简单功能开始逐步迭代遇到问题多查文档和社区你一定能打造出一个独一无二的、好用的QQ机器人伙伴。