做AI项目一年,我最大的感受是:真正能把Agent、大模型、语音识别这些东西串起来练一遍的,往往不是那些一本正经的办公效率工具,反而是看起来“没什么用”的创意项目。最近看到B站AI创造公开赛上有人做了个“没用的AI电子宠物”,标题带着三个问号,点进去却发现这个“没用”的项目,把AI应用开发的完整链路几乎都走了一遍。这篇文章我想借这个项目,聊清楚一个有意思的问题:一个看似无意义的AI电子宠物,背后到底涉及哪些技术,值得开发者关注的地方又在哪里。
先说判断:这类“无用”项目恰恰是学习AI应用开发最好的切入载体。原因很简单,它足够小,小到一个人能掌控全部代码;又足够完整,完整到要处理语音、对话、视觉、状态管理、多模态交互这些真实问题。本文会从一个电子宠物项目的视角,拆解AI应用开发从选题到落地的完整路径,并给出可以直接上手的代码示例和工程建议。
1. 为什么“没用的AI”反而值得做
在技术社区里,我们见过太多“大而全”的AI项目。它们往往挂着一堆新名词,架构图动辄十几个模块,但真正跑起来的效果,可能连一个最简单的对话Demo都不如。而电子宠物这类项目,天然具备几个非常适合练手的特点。
第一,交互链路短。用户看到宠物、说话、宠物回应,整个过程可能只需要三秒钟。这意味着开发者不需要设计复杂的业务流程,可以把全部精力放在“如何让这三秒钟体验更好”上。
第二,反馈极其直观。它回应了、它饿了、它开心了,用户马上就知道系统工作是否正常。这种即时反馈对调试和迭代非常重要——你在改提示词、调模型参数时,立刻能看到效果变化。
第三,情感属性强。电子宠物天然带有情绪价值,用户愿意跟它多聊几句,这正好可以检验LLM在开放域对话中的表现。比起“请回答以下问题”这种生硬评测,让用户自然地和宠物聊天,更能看出大模型在拟人化表达上的真实水平。
第四,多技术环节天然融合。一个电子宠物要真正“活”起来,通常需要:语音识别(听懂用户说话)、大模型对话(生成回应)、语音合成(用声音回应)、视觉能力(识别用户表情或手势)、状态管理(记录宠物心情和成长)。也就是说,你通过这一个项目,就能把AI应用的主流技术栈全部串联起来。
正是这些特点,让电子宠物成为参赛作品中的“潜力股”,也让它成为个人开发者练习AI应用开发的一个很好的起点。
2. AI电子宠物的核心概念与系统组成
在开始写代码之前,有必要先明确一个概念问题:这个项目里的“AI”,到底是用什么技术实现的。
从材料看,这类趣味AI项目的核心通常是调用大模型API,而不是从零训练一个模型。原因是明确的:从零训练语言模型需要海量数据和算力,对个人开发者不现实;而通过API调用成熟的大模型,可以把精力集中在产品创意、交互设计和工程实现上。
一个完整的AI电子宠物系统,大致可以分为六个模块,我整理成了一张表:
| 模块 | 核心职责 | 常见实现方案 | 复杂程度 |
|---|---|---|---|
| 语音识别(ASR) | 把用户说的话转成文字 | 云端ASR API、本地Whisper | 低 |
| 对话引擎(LLM) | 根据宠物人设生成回复 | 大模型API,通过提示词控制 | 中 |
| 语音合成(TTS) | 把文字回话转成语音 | TTS API、Edge TTS | 低 |
| 视觉感知 | 识别用户表情、手势或画面 | 视觉大模型、OpenCV | 中 |
| 状态管理 | 记录宠物健康、心情、成长 | 本地数据库或JSON文件 | 低 |
| 交互终端 | 展示宠物形象、运行交互逻辑 | 网页、桌面应用、硬件终端 | 高 |
其中,最需要花心思的是对话引擎和状态管理,因为这两个模块直接决定了宠物“像不像活的”。
对话引擎的核心不是聊天,而是人设保持。如果只是简单地把用户的话丢给大模型,得到的回复可能是一个AI助手语气,而不是一只宠物。正确的做法是用详细提示词给模型建立“角色身份”,让模型始终以宠物的视角和语气说话。
状态管理的核心是记忆。电子宠物和普通聊天机器人最大的区别在于:它有成长状态。用户早上和晚上看到的宠物应该不一样,它饿了、开心了、长大了,这些都需要被记录并影响后续对话。这意味着我们不能把每次对话都当独立的请求处理,而要在对话中注入宠物当前的状态。
3. 技术选型:纯软件方案还是硬件方案
AI电子宠物有两种主流形态,选择哪种取决于你的目标。
第一种,纯软件方案,比如网页或桌面应用。用户在页面上看到一只宠物形象,通过麦克风或文字输入与宠物互动。这种方案的优势是开发成本低、迭代快,不需要额外硬件,非常适合新手入门和技术验证。
第二种,桌面摆件或硬件方案。比如用树莓派搭配屏幕,或者用MCU驱动一个带屏幕的小设备,让宠物物理意义上“存在”于桌面上。这种方案的优势是形态感强,适合参赛展示,但开发链路更长。
从比赛和博客展示的角度看,更推荐先从纯软件方案做起,后续再迁移到硬件。原因在于:软件方案可以专注打磨对话体验;等对话和状态逻辑稳定后,再做硬件迁移,相当于只是换了前端展示层,核心逻辑可以复用。
技术栈方面,推荐一个入门方案:
| 技术环节 | 推荐方案 |
|---|---|
| 后端语言 | Python 3.11+ |
| Web框架 | FastAPI 或 Flask |
| 前端 | 简单HTML/JS,或原生小程序 |
| 大模型 | 任意支持API调用的Chat模型 |
| 语音识别 | 可选的云端ASR API或Whisper本地方案 |
| 语音合成 | Edge TTS,或TTS API |
| 状态存储 | SQLite本地存储或JSON文件 |
如果你完全没有头绪,先用这个方案准没错。版本细节以实际项目为准,本文重点是通用思路,不需要被单一版本绑住。
4. 电子宠物项目的系统架构设计
在写代码之前,先画出系统架构。对于小型AI应用来说,架构不需要太复杂,但边界要清晰。建议分成三个层次:
接入层,负责接收用户输入。可能是网页上的文字输入框,可能是麦克风采集的语音,也可能是摄像头画面。这一层只负责“收数据”,不负责理解。
逻辑层,是整个项目的核心。它接收用户的输入,结合宠物当前状态,构造提示词,调用大模型API,拿到回复文本,再决定是否调用语音合成。
存储层,负责保存宠物的状态数据。包括宠物名字、等级、饱食度、心情值、用户与宠物的历史对话记录。
这三个层次中,逻辑层最值得注意。很多AI项目的失败,不是因为模型不够强,而是因为逻辑层的粘合代码写得不够好——提示词构建、状态注入、异常处理、回复解析,这些环节任何一个出问题,都会导致最终体验崩坏。
用一句话概括这个架构:接入层负责“听”,存储层负责“记”,逻辑层负责“想”,展示层负责“说”。把这个边界想清楚,代码写起来就顺手很多。
5. AI电子宠物开发的完整流程与核心步骤
接下来是整个项目的实操部分。我会把一个最小可用的电子宠物Demo拆解成五个步骤。
5.1 步骤一:规划宠物的人设
这是最重要的一步,也是很多开发者最容易跳过的一步。大多数人拿到大模型API,第一反应是“快让我调通接口”,结果做出来的AI宠物和通用ChatGPT没有任何区别。
人设规划应该具体到三个层面:
- 身份设定:它是猫、狗、龙,还是一个水滴形的生物?
- 性格特征:是傲娇、粘人、高冷,还是话痨?
- 行为习惯:它喜欢什么话题,会怎么表达饥饿和开心?
这些信息最终要浓缩到一段提示词里。提示词质量直接决定对话效果,实际项目中往往需要反复调整。
5.2 步骤二:搭建后端服务和状态存储
建议用FastAPI搭一个最简后端。目录结构推荐这样:
pet_ai/ ├── main.py # FastAPI主入口 ├── pet_state.py # 宠物状态管理 ├── llm_service.py # 大模型调用封装 ├── prompts.py # 提示词模板 └── requirements.txt # 依赖列表先写宠物状态管理的代码:
# 文件路径:pet_ai/pet_state.py import json import os import time STATE_FILE = "pet_state.json" DEFAULT_STATE = { "name": "小团子", "level": 1, "hunger": 80, "mood": 70, "energy": 60, "birth_time": time.time(), "chat_history": [] } class PetState: def __init__(self, state_file=STATE_FILE): self.state_file = state_file self.state = self.load() def load(self): if os.path.exists(self.state_file): with open(self.state_file, "r", encoding="utf-8") as f: return json.load(f) return DEFAULT_STATE.copy() def save(self): with open(self.state_file, "w", encoding="utf-8") as f: json.dump(self.state, f, ensure_ascii=False, indent=2) def get_state_text(self): s = self.state return ( f"宠物名字:{s['name']},等级:{s['level']}," f"饥饿值:{s['hunger']}/100,心情值:{s['mood']}/100," f"精力值:{s['energy']}/100" ) def append_chat(self, role, content): self.state["chat_history"].append({"role": role, "content": content}) # 只保留最近20条历史,防止提示词过长 self.state["chat_history"] = self.state["chat_history"][-20:] self.save()这段代码的核心是状态持久化。宠物不能和普通聊天机器人一样“每次见面都重新认识”,它的饥饿值、心情值、对话历史都必须被存储下来。
5.3 步骤三:编写人设提示词
提示词是AI电子宠物的灵魂。一个通用的人设提示词模板大概长这样:
# 文件路径:pet_ai/prompts.py PET_SYSTEM_PROMPT = """你是一只电子宠物,你的名字叫{name}。 ## 身份设定 你是一只生活在屏幕里的小团子,圆滚滚的,毛茸茸的,性格黏人又有一点小傲娇。 你非常依赖你的主人,主人一出现你就会很开心。 ## 性格特点 1. 说话简短,通常不超过50个字。 2. 偶尔会撒娇,语气可爱,带点孩子气。 3. 会主动问主人问题,关心主人在做什么。 4. 根据状态数值表现出不同的情绪。 ## 状态信息 {state_text} ## 互动要求 - 如果饥饿值低,你会表达自己饿了,但不要直接索要食物。 - 如果心情值高,你会很活泼,想和主人玩耍。 - 如果精力值低,你会犯困,想休息。 - 对话时不要使用“作为一只电子宠物”这类表述。 - 始终以第一人称说话,语气自然,像真实的小动物。 """ def build_system_prompt(state_text, name): return PET_SYSTEM_PROMPT.format(name=name, state_text=state_text)这里真正容易踩坑的是“不要使用“作为一只电子宠物”这类表述”这句话。不加这句,大模型很容易跳出角色,用第三人称客观描述自己,瞬间破坏沉浸感。这是提示词工程里非常典型的经验。
5.4 步骤四:调用大模型接口
封装一个LLM服务,把系统提示词、状态信息、历史对话合并起来发送给模型:
# 文件路径:pet_ai/llm_service.py import os from openai import OpenAI from prompts import build_system_prompt from pet_state import PetState # 适配任意兼容OpenAI接口的大模型服务 client = OpenAI( api_key=os.getenv("LLM_API_KEY"), base_url=os.getenv("LLM_BASE_URL", "https://api.openai.com/v1") ) def chat_with_pet(user_input: str, pet: PetState): system_prompt = build_system_prompt( pet.get_state_text(), pet.state["name"] ) messages = [{"role": "system", "content": system_prompt}] # 注入最近的历史对话 for item in pet.state["chat_history"][-5:]: messages.append(item) messages.append({"role": "user", "content": user_input}) response = client.chat.completions.create( model=os.getenv("LLM_MODEL", "gpt-4o-mini"), messages=messages, temperature=0.8, max_tokens=300 ) reply = response.choices[0].message.content.strip() # 记录对话 pet.append_chat("user", user_input) pet.append_chat("assistant", reply) return reply这里采用的环境变量读取方式,是为了避免把密钥写到代码里。temperature设为0.8是为了让回复带一点随机性,使宠物显得更鲜活。如果设成0,宠物会变成一台没有感情的问答机器。
5.5 步骤五:写主入口实现接口
最后写一个FastAPI应用,向外部暴露聊天接口。为了降低上手门槛,我们先不做语音,直接使用文本对话:
# 文件路径:pet_ai/main.py from fastapi import FastAPI from pydantic import BaseModel from llm_service import chat_with_pet from pet_state import PetState app = FastAPI() pet = PetState() class ChatRequest(BaseModel): message: str @app.get("/pet/state") def get_pet_state(): return pet.state @app.post("/pet/chat") def chat(req: ChatRequest): reply = chat_with_pet(req.message, pet) return {"reply": reply, "state": pet.state} if __name__ == "__main__": import uvicorn uvicorn.run(app, host="0.0.0.0", port=8000)这个接口返回两个东西:宠物回复、宠物最新状态。前端可以拿状态数据做可视化,比如心情值低时显示哭泣的宠物形象。
6. 进阶功能:语音交互与多模态体验
完成了文本版电子宠物之后,可以考虑为它加上语音能力。语音交互会让宠物变得“活”起来,更像一个真实存在的伙伴。
6.1 语音识别
如果选择云端ASR方案,常见做法是把麦克风采集的音频传到服务端,返回文字结果。开发阶段也可以先用Whisper的本地方案,方便离线调试。需要注意麦克风权限和音频格式转换的问题,这是新手最容易卡住的地方。
6.2 语音合成
语音合成相对简单,Edge TTS之类的方案可以直接把文本转成MP3音频,返回给前端播放。为了让宠物更有个性,可以选择不同的音色,例如可爱一点的声线,让听觉体验统一。
语音交互引入后,核心链路就变成:
用户说话 -> 语音识别 -> 文本 -> 大模型回复 -> 文本 -> 语音合成 -> 宠物说话这条链路每一步的延迟都要控制好,否则用户说完一句话要等五秒才听到回应,体验会大打折扣。通常建议把可缓存的环节缓存掉,并且在架构允许的情况下采用流式输出。关于流式与实时性优化,后面单独展开。
6.3 视觉交互
想做多模态的开发者,还可以加入简单的视觉能力。比如通过摄像头识别用户是否在微笑,如果在微笑,宠物心情值就上涨;识别到用户做作业或加班,宠物会安慰一句“你真辛苦”。
这部分的实现可以依赖视觉大模型API,把摄像头画面传过去,让模型返回场景描述。但要注意隐私问题,如果只是比赛Demo,务必要在界面上提醒用户摄像头将被调用,并提供关闭功能。
7. 功能验证与效果调优
项目写完后,不能只看“能跑”就收工。AI应用的质量是试出来的。
建议做一轮功能验证,重点测试以下场景:
| 测试场景 | 输入示例 | 预期表现 |
|---|---|---|
| 基础对话 | “你是谁呀?” | 用宠物身份介绍自己,不暴露AI身份 |
| 状态响应(饥饿) | “你饿不饿?” | 根据饥饿值做出对应回复 |
| 性格一致性 | 连续聊10句 | 语气、人设保持稳定,不跳戏 |
| 历史记忆 | “你还记得我叫什么吗?” | 能引用之前的对话内容 |
| 异常输入 | 突然发一段乱码 | 宠物自然应对,不崩溃、不报错 |
如果发现以下问题,可以按对应方向优化:
- 人设不统一:说明系统提示词不够强,或历史对话太杂。可以考虑固定前几条系统级对话样本(few-shot),也可以提高system prompt的权重描述。
- 回复太长:在提示词中限制“不超过50个字”,或在接口参数中设置
max_tokens。 - 状态影响不明显:说明宠物看不懂数值变化。极端值(比如饥饿值低于20)应该在提示词里写得更明确,让模型做出更强反馈。
- 多轮对话后逻辑混乱:检查
chat_history截断逻辑,太长会稀释关键信息,太短又记不住事情,需要根据模型上下文长度做平衡。
8. 常见问题与排查方法
开发过程中最容易遇到的问题,我整理成了一张排查表:
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
| 启动报错“module not found” | 依赖未安装 | 查看控制台完整报错 | pip install -r requirements.txt |
| 调用大模型API超时 | 网络环境或API地址配置错误 | 单独测试API连通性 | 检查LLM_BASE_URL和网络环境 |
| 宠物回复语气像AI助手 | 系统提示词强度不够 | 打印实际发给模型的messages,检查system提示词是否被正确拼入 | 强化人设描述,补充禁止表达 |
| 宠物不记得之前的对话 | 历史未注入或存储失败 | 查看pet_state.json是否有内容 | 检查append_chat调用位置 |
| 回复速度太慢 | 模型响应慢或网络慢 | 拆分延迟环节 | 使用更小模型、开启流式输出 |
| 中文回复乱码 | 编码问题 | 检查控制台编码 | 统一UTF-8编码 |
| 宠物状态数值变化不符合预期 | 逻辑层没有更新状态 | 检查状态更新函数的触发点 | 在关键节点显式调用状态更新 |
排查的核心原则是:先定位是哪一层出了问题,再动手改代码。如果是网络问题,不应该改提示词;如果提示词没拼入状态,也不该调模型参数。
这里再补充一个容易忽略的问题:系统提示词跟历史文本的“状态实时性”存在冲突。你可以在一次请求的前面注入了包含最新状态的系统提示词,但对话历史里可能还有宠物说自己“刚吃完饭”的旧话。矛盾信息会让模型混乱,实际项目中通常要在历史记录里做状态摘要替换,让模型始终参考最新状态,而不是被旧对话带偏。
9. 工程化与生产环境的最佳实践
如果这个项目不只是参赛Demo,而是准备长期维护,或者你想把它扩展成真正能服务用户的AI应用,那么下面这几点值得留意。
9.1 提示词和代码分离管理
把提示词单独放到一个prompts.py或外部配置文件中,不要直接硬编码在业务逻辑里。AI项目的提示词一定会频繁调整,每次都改代码再重启服务,既低效又容易出错。更进一步,团队里可以维护一个提示词版本管理机制,方便回滚。
9.2 增加关键节点日志
大模型调用天然是黑盒,不打印日志你就无法排查问题。至少需要记录:每轮请求的时间、消耗的token数、模型返回的结果、异常信息。这样既方便调优,也方便统计成本。
import logging logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s") logger = logging.getLogger(__name__) # 在 llm_service 中调用 logger.info("user: %s | reply: %s | tokens: %s", user_input, reply, response.usage)9.3 控制API成本
电子宠物如果一直挂在公网给别人玩,token消耗会非常快。建议:
- 使用较便宜的模型版本进行对话。
- 历史对话做裁剪,不要无限制累积。
- 设置单用户每日调用上限。
- 对低质量回复设置兜底,避免连续重试。
9.4 增加安全兜底
这是AI应用容易被忽略、但绝对不能省略的一环。大模型输出无法做到100%可控,需要做一层输出安全过滤。对于宠物类应用,常见的做法包括:敏感词过滤、输出内容长度限制、失败时返回预设的宠物俏皮话(例如:“主人我刚才发呆走神了,你能再说一遍吗?”)。用兜底回复有一个额外的好处:遇到大模型超时或API异常时,用户体验不会直接断裂。
9.5 状态管理要考虑并发
如果宠物同时被多个用户访问,简简单单的一个JSON文件就不够用了。那时需要切换到真正的数据库,并考虑每个用户对应一只独立宠物,而不是所有用户共享一只。这个扩展点是比赛Demo与真实产品的重要分水岭。
10. 可供参考的方向:让“没用”变成“有趣”
最后回到标题里的问题:电子宠物还能怎么玩?一个“没用的AI”做到什么程度才算好?
从材料看,这类比赛项目的核心已经不是“模型有多强”,而是“创意有多巧、实现有多完整”。同样调用一个GPT级别的模型,有人做出来的是又一个聊天窗口,有人做出来的是有生命感的伙伴。差距不在模型,在产品设计。
如果你也想做自己的AI电子宠物,建议从这些方向入手做差异化:
- 设定更有人情味:给宠物赋予背景故事,甚至让它有“成长烦恼”。
- 把状态做出画面感:饥饿值低时宠物在屏幕上蔫蔫的,开心时打滚卖萌,这比纯文字反馈有效得多。
- 结合场景:番茄钟结束后宠物出来鼓励你,工作疲惫时宠物提醒你喝水。
- 让宠物有“小脾气”:久不互动会生气,经常陪伴会长大。这个层面的情绪反馈,是用户留存的关键。
AI技术发展到今天,做一个“有用”的工具已经不难,难的是在工具之上,让用户感到“被陪伴、被理解”。电子宠物的价值不在它是不是一个生产力工具,而在于它用最轻的方式,让用户感受到了AI的温度。
对一个开发者来说,完成这样一个项目的收获也是相似的:你不仅跑通了大模型、语音、状态管理这条完整链路,更重要的是,你知道如何把一个模糊的创意,一步步变成可运行、可感知、可迭代的产品。这恰恰是很多高深技术教程教不了的东西。
如果你看了这篇文章也有了做一只电子宠物的冲动,我建议你先从最小版本开始:不接语音,不做视觉,只有一只会聊天、会饿、会开心的文本宠物。把它跑通,再一步步让它“活”起来。做一个“没用的项目”,本身可能就是最有用的AI入门课。