1. 项目概述一个面向开发者的开源机器人框架最近在GitHub上闲逛发现了一个挺有意思的项目叫“Maliot100X/ZooBot”。光看名字你可能会联想到动物园或者动物机器人但实际上它是一个面向开发者的、高度可扩展的开源机器人框架。简单来说它不是一个成品机器人而是一个“工具箱”或者说“脚手架”旨在帮助开发者快速构建、测试和部署各种类型的机器人应用无论是聊天机器人、自动化任务机器人还是更复杂的交互式智能体。这个项目的核心价值在于“Zoo”这个字眼。它试图构建一个“机器人动物园”将不同功能、不同协议的机器人“物种”管理起来提供一套统一的开发、配置和运行范式。对于我这样经常需要折腾各种机器人来辅助开发、测试或者自动化流程的人来说这种统一管理的思路非常有吸引力。它能解决几个常见的痛点不同机器人项目依赖环境混乱、配置分散难以复用、部署流程不统一、以及缺乏一个中心化的管理和监控界面。如果你是一名开发者无论是想快速搭建一个Slack/Discord通知机器人还是想构建一个集成了多种AI模型如GPT、Claude的智能客服系统亦或是需要管理一批执行定时任务的自动化脚本BotZooBot都提供了一个不错的起点。它抽象了机器人的通用生命周期初始化、配置加载、事件处理、消息响应、状态管理、优雅退出等让开发者可以更专注于业务逻辑的实现而不是重复造轮子。2. 核心架构与设计哲学拆解要理解ZooBot首先得拆解它的设计思路。一个好的框架其价值往往体现在它对复杂性的封装和对通用模式的抽象上。2.1 “动物园”式的模块化管理“ZooBot”这个名字非常形象地揭示了其核心架构思想每个机器人都是一个独立的“动物”而ZooBot就是管理这些动物的“动物园”。在这个比喻下动物Bot代表一个具有特定功能的机器人实例。比如一个负责从GitHub抓取issue的爬虫机器人一个在Discord频道里回答问题的客服机器人。笼舍Bot 运行时环境ZooBot为每个机器人提供独立的运行环境。这通常意味着隔离的配置、独立的消息循环或事件处理器甚至可以是独立的进程或容器。这保证了机器人之间的隔离性一个机器人的崩溃不会影响整个“动物园”。饲养员框架核心ZooBot的核心框架充当了饲养员的角色。它负责所有机器人的生命周期管理——启动、停止、重启、健康检查、日志收集和资源分配。通用饲料与技能插件与中间件不同的动物可能需要不同的“饲料”依赖库和“技能”功能模块。ZooBot通过插件Plugin或中间件Middleware机制来提供这些可复用的能力。例如一个“HTTP请求插件”可以被多个需要访问网络的机器人共用一个“消息格式化中间件”可以在所有聊天机器人消息发送前统一处理。这种设计带来的直接好处是高内聚、低耦合。每个机器人的功能集中在其自身模块内通过清晰的接口与框架和其他机器人交互。新增一个机器人就像往动物园里引入一个新物种只需要定义好它的“习性”配置和业务逻辑而不需要改动动物园的基础设施。2.2 配置驱动与约定优于配置现代应用框架普遍采用“配置驱动”和“约定优于配置”的原则ZooBot也不例外。通过解析项目结构我发现它通常期望开发者按以下方式组织项目zoo_bot_project/ ├── bots/ # 存放所有机器人定义 │ ├── github_tracker.py │ ├── discord_helper.py │ └── __init__.py ├── configs/ # 配置文件目录 │ ├── default.yaml │ └── production.yaml ├── plugins/ # 公共插件目录 │ └── http_client.py ├── main.py # 应用入口启动ZooBot框架 └── requirements.txt # 项目依赖配置驱动机器人的行为如API密钥、监听地址、触发关键词、定时任务周期等都被抽取到配置文件如YAML、JSON或环境变量中。这使得同一份机器人代码可以在不同环境开发、测试、生产下通过切换配置来运行也便于进行安全的密钥管理。约定优于配置框架会定义一些默认约定。例如bots目录下的Python文件如果导出了一个符合特定接口如继承自BaseBot类的类框架会自动发现并将其注册为可用的机器人。这减少了大量样板代码和繁琐的注册声明。2.3 事件驱动与消息总线机器人本质上是事件响应器。它监听外部事件如收到一条消息、一个HTTP请求、一个定时器触发然后执行相应的处理逻辑并可能产生响应动作。ZooBot框架内部通常会实现一个轻量级的事件驱动架构或消息总线。事件Event任何需要机器人关注的事情都被抽象为事件。例如MessageReceivedEvent、TimerEvent、SystemStartupEvent。处理器Handler每个机器人会注册一个或多个事件处理器。当特定类型的事件被发布到总线上时框架会将其路由到注册了该事件处理器的机器人。消息总线Event Bus作为中枢负责事件的发布与订阅。它解耦了事件产生者和消费者机器人。一个机器人产生的事件可以被其他机器人消费从而实现机器人间的协同工作。例如一个GitHub机器人监听到新的PR事件可以发布一个PRCreatedEvent然后一个通知机器人消费这个事件将其转发到团队聊天室。这种架构让机器人的逻辑变得清晰——你只需要关心“当XX事件发生时我要做什么”。框架负责复杂的事件路由、异步处理和错误管理。3. 从零开始构建你的第一个ZooBot机器人理论讲得再多不如动手实践。下面我将带你一步步创建一个最简单的“回声机器人”EchoBot它会在收到特定指令后原样回复消息。这个过程会揭示ZooBot开发的核心流程。3.1 环境准备与项目初始化首先确保你的开发环境已经就绪。我推荐使用Python 3.8因为它有良好的异步支持而现代机器人框架大量依赖asyncio。# 1. 创建项目目录并进入 mkdir my_echo_zoo cd my_echo_zoo # 2. 创建虚拟环境强烈推荐避免依赖污染 python -m venv venv # 3. 激活虚拟环境 # 在Windows上: venv\Scripts\activate # 在Mac/Linux上: source venv/bin/activate # 4. 假设ZooBot已发布到PyPI安装框架核心库 # 注意由于“Maliot100X/ZooBot”是一个GitHub项目名其PyPI包名可能不同这里我们用假设的包名。 # 实际中你需要查看项目的README可能是 pip install zoobot 或通过git安装。 # 这里我们模拟安装一个机器人框架基础包。 pip install some-bot-framework # 此处为示例请替换为实际包名或安装方式 # 5. 创建项目基础结构 mkdir bots configs plugins touch main.py configs/default.yaml requirements.txt接下来编辑requirements.txt至少包含你安装的框架包。然后编辑configs/default.yaml添加最基本的配置# configs/default.yaml bot: name: MyZoo log_level: INFO bots: echo_bot: enable: true type: echo.EchoBot # 指向我们即将创建的机器人类 config: trigger_command: !echo # 触发回声的指令3.2 定义第一个机器人EchoBot现在在bots目录下创建echo.py文件定义我们的机器人。# bots/echo.py import logging from some_bot_framework import BaseBot, MessageEvent # 导入框架基类和事件类 logger logging.getLogger(__name__) class EchoBot(BaseBot): 一个简单的回声机器人。 def __init__(self, name, config): # 调用父类初始化 super().__init__(name) # 从配置中读取触发指令 self.trigger_command config.get(trigger_command, !echo) logger.info(fEchoBot {name} 初始化完成触发指令: {self.trigger_command}) async def on_message(self, event: MessageEvent): 当收到消息事件时触发。 event对象通常包含消息内容、发送者、频道等信息。 message_content event.content.strip() # 检查消息是否以我们的触发指令开头 if message_content.startswith(self.trigger_command): # 提取要回声的内容去掉触发指令 text_to_echo message_content[len(self.trigger_command):].strip() if not text_to_echo: reply 你好请告诉我你想让我回声什么内容。 else: reply f你说了: {text_to_echo} # 调用框架方法发送回复 await self.send_message(event.channel_id, reply) logger.debug(fEchoBot 回复了消息: {reply}) async def start(self): 机器人启动时调用可用于初始化连接等。 logger.info(fEchoBot {self.name} 启动。) # 这里可以添加连接消息平台如WebSocket的代码 # 对于简单的示例我们可能依赖框架的事件推送所以可能什么都不用做。 async def stop(self): 机器人停止时调用用于清理资源。 logger.info(fEchoBot {self.name} 停止。)关键点解析继承BaseBot这是框架的约定。基类提供了机器人生命周期管理的基础方法和属性。__init__方法用于初始化接收机器人的名字和专属配置。这里我们读取了配置中的trigger_command。on_message方法这是一个事件处理器。框架在收到消息事件时会调用所有注册了该事件处理器的机器人的on_message方法。我们在这里实现业务逻辑判断是否是指令然后组织回复。send_message方法这是基类或框架上下文提供的方法用于发送消息。它抽象了底层通信协议HTTP、WebSocket等我们只需要关心内容和目标。start/stop方法用于处理机器人的启动和停止逻辑例如建立或断开网络连接。3.3 组装与启动main.py最后我们需要一个入口点来组装并启动整个“动物园”。编辑main.py# main.py import asyncio import yaml # 需要安装PyYAML: pip install pyyaml import logging from pathlib import Path # 假设框架的App类是启动入口 from some_bot_framework import App # 设置日志 logging.basicConfig(levellogging.INFO, format%(asctime)s - %(name)s - %(levelname)s - %(message)s) async def main(): # 1. 加载配置文件 config_path Path(__file__).parent / configs / default.yaml with open(config_path, r, encodingutf-8) as f: config yaml.safe_load(f) # 2. 创建应用实例传入配置 app App(config) # 3. 自动发现并注册bots目录下的机器人 # 框架通常有自动发现机制这里我们演示手动注册如果自动发现未生效 # 首先确保bots模块可以被导入 import sys sys.path.insert(0, str(Path(__file__).parent)) try: from bots.echo import EchoBot # 假设框架的注册方式是通过配置这里只是示意。 # 实际中配置文件的 bots.echo_bot.type: echo.EchoBot 已经指明了类路径。 # 框架会动态导入并实例化。 logger.info(机器人模块导入成功。) except ImportError as e: logger.error(f导入机器人模块失败: {e}) return # 4. 启动应用框架会负责加载配置、实例化机器人、启动事件循环等 try: await app.run() except KeyboardInterrupt: logger.info(接收到中断信号开始优雅关闭...) finally: await app.cleanup() if __name__ __main__: asyncio.run(main())3.4 运行与测试一切就绪后就可以运行了。python main.py如果一切正常日志会显示应用启动EchoBot初始化完成。现在你需要一个方式来向它发送消息事件。在真实场景中这需要一个“适配器”Adapter比如一个Discord机器人客户端、一个HTTP服务器或一个命令行接口。为了快速测试我们可以模拟一个消息事件。修改main.py在app.run()之前或之后添加一段测试代码生产环境需移除# ... 在 app.run() 之后实际中可能需要更复杂的集成测试 # 注意这只是一个概念演示实际框架的测试方式可能不同。 logger.info(模拟测试...) # 假设框架提供了测试工具或我们可以直接调用事件总线 # await app.event_bus.publish(MessageEvent(content!echo Hello ZooBot, channel_idtest)) logger.info(测试完成。)实操心得依赖管理与虚拟环境机器人项目常常依赖许多特定版本的库比如某个聊天平台的SDK。务必使用虚拟环境venv, conda, poetry等来隔离项目依赖。requirements.txt或pyproject.toml文件必须精确。我曾经因为一个间接依赖的版本升级导致整个机器人瘫痪排查了半天。建议在requirements.txt中主要依赖使用固定版本次要依赖可以使用但最好也记录下测试通过的版本。4. 核心功能扩展插件、中间件与持久化一个简单的回声机器人只是开始。ZooBot框架的强大之处在于其可扩展性。让我们看看如何为其添加更高级的功能。4.1 开发一个自定义插件天气查询假设我们想让EchoBot具备查询天气的功能。我们可以创建一个天气插件让多个机器人共享。# plugins/weather.py import aiohttp import logging from typing import Optional logger logging.getLogger(__name__) class WeatherPlugin: 天气查询插件。 def __init__(self, api_key: str): if not api_key: raise ValueError(WeatherPlugin 需要有效的 API Key。) self.api_key api_key self.base_url http://api.weather.com/v3 # 示例URL请替换为真实服务 self.session: Optional[aiohttp.ClientSession] None async def start(self): 插件启动创建HTTP会话。 self.session aiohttp.ClientSession() logger.info(WeatherPlugin 已启动。) async def stop(self): 插件停止关闭会话。 if self.session: await self.session.close() logger.info(WeatherPlugin 已停止。) async def get_weather(self, city: str) - dict: 查询指定城市的天气。 if not self.session: raise RuntimeError(WeatherPlugin 尚未启动。) params {city: city, key: self.api_key} try: async with self.session.get(f{self.base_url}/current, paramsparams) as resp: resp.raise_for_status() data await resp.json() return data except aiohttp.ClientError as e: logger.error(f查询天气失败: {e}) return {error: 网络请求失败} except Exception as e: logger.error(f处理天气数据失败: {e}) return {error: 数据处理失败}然后在EchoBot中集成这个插件。首先需要在配置中增加插件的配置并在框架启动时初始化插件。通常框架会有插件加载机制。这里我们演示如何在机器人内部手动集成假设插件实例通过依赖注入或全局上下文获取# bots/echo.py (修改部分) class EchoBot(BaseBot): def __init__(self, name, config, weather_pluginNone): # 接收插件实例 super().__init__(name) self.trigger_command config.get(trigger_command, !echo) self.weather_plugin weather_plugin # 持有插件引用 self.weather_command config.get(weather_command, !weather) async def on_message(self, event: MessageEvent): message_content event.content.strip() if message_content.startswith(self.trigger_command): # ... 原有的回声逻辑 ... elif self.weather_plugin and message_content.startswith(self.weather_command): # 处理天气查询指令例如 “!weather 北京” city message_content[len(self.weather_command):].strip() if not city: await self.send_message(event.channel_id, 请输入城市名例如!weather 北京) return weather_info await self.weather_plugin.get_weather(city) if error in weather_info: reply f查询天气失败{weather_info[error]} else: # 简化处理实际应解析返回的JSON reply f{city}的天气是{weather_info.get(condition, 未知)}温度{weather_info.get(temp, N/A)}°C await self.send_message(event.channel_id, reply)4.2 使用中间件实现全局功能中间件Middleware是另一种强大的扩展机制它在请求/事件处理的管道中发挥作用可以用于实现日志记录、权限校验、频率限制、消息预处理等横切关注点。例如实现一个简单的日志记录中间件记录所有进出消息# middleware/logger_middleware.py import logging import time from some_bot_framework import BaseMiddleware, MessageEvent logger logging.getLogger(__name__) class LoggerMiddleware(BaseMiddleware): 日志中间件记录所有消息事件的处理时间和结果。 async def on_event_preprocess(self, event): 在事件被机器人处理之前调用。 if isinstance(event, MessageEvent): event.metadata[process_start] time.time() logger.info(f收到消息事件: 发送者{event.sender}, 内容{event.content[:50]}...) return event async def on_event_postprocess(self, event, responses): 在事件被机器人处理之后调用。 if isinstance(event, MessageEvent): duration time.time() - event.metadata.get(process_start, time.time()) logger.info(f消息事件处理完成耗时{duration:.3f}秒生成{len(responses)}个响应。) return responses在框架配置中注册这个中间件它就会自动对流经的所有MessageEvent生效无需修改每个机器人的代码。4.3 状态持久化让机器人记住上下文简单的机器人是无状态的每次交互都是独立的。但很多场景需要机器人记住一些信息比如用户的偏好、对话的上下文、任务的状态等。这就需要持久化。ZooBot框架通常不强制规定持久化方案但会提供钩子或接口。常见的做法有数据库集成在机器人内部连接SQLite轻量、PostgreSQL或MongoDB等数据库。例如为用户设置一个“昵称”。# 在机器人内部 import sqlite3 class UserPrefBot(BaseBot): def __init__(self, name, config): super().__init__(name) self.db_path config.get(db_path, bot_data.db) self._init_db() def _init_db(self): conn sqlite3.connect(self.db_path) cursor conn.cursor() cursor.execute(CREATE TABLE IF NOT EXISTS user_prefs (user_id TEXT PRIMARY KEY, nickname TEXT)) conn.commit() conn.close() async def set_nickname(self, user_id, nickname): conn sqlite3.connect(self.db_path) cursor conn.cursor() cursor.execute(REPLACE INTO user_prefs (user_id, nickname) VALUES (?, ?), (user_id, nickname)) conn.commit() conn.close()利用框架的存储抽象更优雅的方式是使用框架可能提供的存储抽象层。例如框架提供一个Storage接口背后可以是内存、Redis或数据库。这样机器人的代码就不需要关心具体的存储实现。外部状态管理对于复杂状态可以考虑使用专门的状态管理服务或数据库机器人通过API与之交互。注意事项并发与状态安全如果你的机器人是多实例运行或者处理高并发请求直接使用像SQLite这样的文件数据库在没有写同步的情况下可能会遇到锁问题。对于生产环境考虑使用客户端-服务器模式的数据库如PostgreSQL或者利用框架的上下文确保状态操作是线程/进程安全的。对于简单的键值存储Redis是一个极佳的选择它性能高并且天然支持分布式场景。5. 部署与运维让机器人稳定运行开发完成只是第一步让机器人7x24小时稳定可靠地运行才是真正的挑战。ZooBot框架的设计通常考虑到了部署需求。5.1 部署方式选型直接进程运行最简单的方式在服务器上通过python main.py或nohup python main.py 运行。仅适用于测试和极低负载场景。进程崩溃后不会自动重启日志管理也不方便。使用进程管理工具systemd(Linux)创建.service文件可以设置开机自启、自动重启、资源限制、日志重定向到journal。这是生产环境最常用的方式之一。# /etc/systemd/system/my-zoo-bot.service [Unit] DescriptionMy ZooBot Service Afternetwork.target [Service] Typesimple Userbotuser WorkingDirectory/opt/my-zoo-bot ExecStart/opt/my-zoo-bot/venv/bin/python main.py Restarton-failure RestartSec5 [Install] WantedBymulti-user.targetSupervisor一个纯Python的进程管理工具配置简单有Web管理界面。同样支持自动重启、日志管理。容器化部署Docker这是目前最主流和推荐的方式。将你的ZooBot应用及其所有依赖打包成一个Docker镜像。# Dockerfile FROM python:3.10-slim WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY . . CMD [python, main.py]优点环境一致隔离性好易于水平扩展结合KubernetesCI/CD集成方便。云函数/Serverless如果你的机器人是事件驱动、无状态或状态外置的可以考虑部署为云函数如AWS Lambda Google Cloud Functions。框架需要适配这种事件触发的运行模式通常是通过一个HTTP适配器。这种方式成本低无需管理服务器但冷启动可能带来延迟。5.2 配置管理与密钥安全绝对不要将API密钥、数据库密码等敏感信息硬编码在代码或提交到版本库中。正确做法是使用环境变量或加密的配置文件。环境变量在configs/default.yaml或代码中通过os.getenv(API_KEY)读取。# configs/production.yaml weather: api_key: ${WEATHER_API_KEY} # 使用模板变量由部署工具替换在启动前设置环境变量export WEATHER_API_KEYyour_key_here。在Docker中可以使用-e参数或secrets管理。密钥管理服务在生产环境中可以使用如HashiCorp Vault、AWS Secrets Manager等服务来动态获取密钥进一步提高安全性。5.3 监控与日志没有监控的线上服务就像在黑暗中飞行。日志确保框架和你的机器人代码都使用了合理的日志级别INFO, ERROR, DEBUG。日志应输出到标准输出stdout然后由Docker或进程管理器收集并转发到像ELK Stack、Loki或云日志服务中方便聚合和查询。健康检查为你的ZooBot主服务添加一个健康检查端点如/health返回应用状态、依赖服务状态数据库、API等。这在容器编排K8s中尤为重要。指标监控使用Prometheus客户端库暴露应用指标如处理消息总数、平均响应时间、错误率等。结合Grafana进行可视化。告警基于日志错误日志突增和指标响应时间超阈值设置告警及时通知负责人。实操心得日志分级与上下文打日志时一定要带上足够的上下文信息比如当前机器人的名字、正在处理的事件ID、用户ID等。这能在排查问题时帮你快速定位。我习惯在框架的上下文Context或请求/事件对象中注入一个唯一的request_id或event_id并让它贯穿整个处理链路在所有相关的日志行中都打印出来。这样无论日志多么分散你都能轻松地拼凑出一次完整请求的处理轨迹。6. 常见问题与排查技巧实录在实际开发和运维中你肯定会遇到各种问题。下面记录了一些典型场景和我的排查思路。6.1 机器人不响应消息这是最常见的问题。可以按照以下清单排查问题可能点排查步骤解决方法配置错误检查配置文件确认目标机器人enable: true且type路径正确。修正配置确保类路径能被Python正确导入。事件未触发检查日志看框架是否收到了原始事件如HTTP请求、WebSocket消息。确认消息来源适配器是否正确配置并运行消息格式是否符合框架预期。处理器未注册在机器人start方法或日志中确认on_message等处理器已成功注册到框架的事件总线。检查机器人基类的初始化逻辑或手动在start中调用框架的注册方法。指令匹配失败在on_message方法开始处打印收到的message_content检查是否与你的trigger_command匹配注意空格、大小写。调整字符串匹配逻辑或使用更灵活的正则表达式。异步阻塞检查on_message内部是否有同步的耗时操作如time.sleep, 同步网络请求阻塞了事件循环。将同步操作改为异步用asyncio.sleep,aiohttp等或使用run_in_executor放到线程池中执行。权限或令牌失效对于需要认证的平台如Discord, Slack检查机器人令牌是否有效、是否有相应权限读取消息、发送消息。在平台开发者后台重置令牌检查权限范围。6.2 内存泄漏或CPU占用过高长时间运行后机器人占用资源越来越多。排查工具使用psutil库在健康检查端点暴露内存和CPU信息。使用objgraph或tracemalloc进行内存快照对比查找增长的对象。常见原因全局变量或缓存无限增长例如在内存中保存了所有历史消息而未清理。需要设置缓存过期策略或使用LRU缓存functools.lru_cache。未关闭的资源如aiohttp ClientSession、数据库连接、文件句柄。确保在stop方法或使用async with上下文管理器正确关闭。循环引用特别是在自定义复杂对象时。Python的GC能处理大部分但涉及__del__或C扩展时可能有问题。使用gc.collect()并检查gc.garbage。日志级别过低DEBUG级别的日志在高速处理消息时会产生海量输出消耗IO和CPU。生产环境应使用INFO或WARNING级别。6.3 网络依赖服务不稳定机器人依赖的外部API如天气API、翻译API可能超时或失败。重试机制为网络请求添加指数退避重试。可以使用tenacity库。from tenacity import retry, stop_after_attempt, wait_exponential retry(stopstop_after_attempt(3), waitwait_exponential(multiplier1, min2, max10)) async def call_external_api(url): async with aiohttp.ClientSession() as session: async with session.get(url) as resp: resp.raise_for_status() return await resp.json()超时设置为所有外部请求设置合理的超时时间避免一个慢请求拖垮整个事件循环。async with aiohttp.ClientSession(timeoutaiohttp.ClientTimeout(total10)) as session: ...降级策略当外部服务不可用时提供降级响应。例如天气API失败时回复“天气服务暂时不可用请稍后再试”而不是抛出异常导致机器人崩溃。6.4 多机器人协同与事件循环阻塞当你有多个机器人其中一个执行了耗时任务可能会阻塞整个框架的事件循环导致其他机器人也“卡住”。识别阻塞使用asyncio的调试工具或观察日志中不同机器人的处理时间是否出现异常交叉。解决方案彻底异步化确保所有I/O操作都使用异步库aiohttp,aiomysql,asyncpg等。将CPU密集型任务移交线程池使用asyncio.to_thread()或loop.run_in_executor。import asyncio def cpu_intensive_task(data): # 这里是同步的CPU密集型计算 return result async def handle_event(event): # 将任务放到线程池中执行不阻塞事件循环 result await asyncio.to_thread(cpu_intensive_task, event.data) await self.send_message(event.channel_id, result)任务隔离对于特别耗时的独立任务可以考虑将其拆分为独立的微服务或Worker进程通过消息队列如Redis, RabbitMQ与主机器人通信。开发像ZooBot这样的机器人框架项目最大的乐趣在于构建一个灵活、健壮的生态系统。从设计清晰的架构到实现一个个具体的机器人功能再到处理各种棘手的运行时问题整个过程充满了挑战和成就感。记住从简单开始迭代优化。先让一个核心机器人跑起来再逐步添加插件、中间件和完善部署运维体系。多看看框架本身的源码和社区其他优秀机器人的实现能学到很多最佳实践。最重要的是享受创造的过程让你的“机器人动物园”日益繁荣。