基于 hello_agents 构建"猜猜我是谁"交互式猜人物游戏:LLM 角色扮演、搜索增强与语义匹配的完整实战
【免费下载链接】hello-agents📚 《从零开始构建智能体》——从零开始的智能体原理与实践教程项目地址: https://gitcode.com/GitHub_Trending/he/hello-agents
本篇文章以开源仓库 hello-agents 中Co-creation-projects/afei-GuessWhoAmI子项目为蓝本,系统拆解如何基于hello_agents框架从零搭建一个由 LLM 驱动、前后端完整的交互式猜人物游戏:Agent 随机扮演历史人物、神话人物或网络红人,玩家通过多轮对话提问、有限次提示与语义猜测来揭开谜底。读完本文,你将掌握 LLM 动态人物生成、Tavily 搜索增强提示、SimpleAgent 角色扮演、LLM 语义匹配与 Wikipedia 图片检索这一整套可复用的 Agent 应用开发方案。
项目定位与核心设计亮点
GuessWhoAmI(猜猜我是谁)是一个基于hello_agents框架开发的交互式猜人物游戏。AI Agent 随机扮演一位历史人物、神话人物或网络红人,用户通过多轮对话提问来猜测其身份。项目在 README 中总结了六大特色:
- 🤖LLM 动态生成人物—— 每局由大模型随机生成人物,涵盖中西历史、神话、虚构角色、网络红人等多个领域,不重复;
- 🎭沉浸式角色扮演—— Agent 以第一人称扮演人物,语气符合其性格与时代背景,回答具有迷惑性和引导性;
- 🔍Tavily 搜索增强—— 自动搜索人物资料,生成由模糊到具体的 3 条提示;
- 🖼️猜对后展示人物图片—— 猜对后通过 Wikipedia 搜索并展示人物图片;
- 🧠语义猜测匹配—— 使用 LLM 语义判断猜测是否正确,支持别名、外号等多种表达;
- ⚡FastAPI 高性能后端+ 现代化 Web 前端。
从架构角度看,这个项目最值得学习的地方在于:它把"随机性注入"(防人物重复)、"外部搜索工具"(Tavily)、"无工具纯对话 Agent"(角色扮演)与"LLM 作为判断器"(语义匹配)四种 Agent 能力组合在一条完整的游戏流水线中,是hello_agents框架多工具协同的典型范例。
项目结构与模块划分
afei-GuessWhoAmI/ ├── restart.sh # 一键启动脚本(前后端) ├── backend/ │ ├── main.py # FastAPI 入口,API 路由 │ ├── agents.py # Agent 核心逻辑(人物生成、角色扮演、猜测判断) │ ├── game_logic.py # 游戏状态管理(GameSession) │ ├── config.py # 配置管理(Settings 单例) │ ├── models.py # Pydantic 请求/响应模型 │ ├── requirements.txt # Python 依赖 │ ├── .env.example # 环境变量模板 │ └── tools/ │ ├── tavily_search_tool.py # Tavily 搜索工具(生成提示) │ └── search_image_tool.py # Wikipedia 图片搜索工具 ├── frontend/ │ ├── index.html # 主页面 │ ├── style.css # 样式文件 │ └── app.js # 交互逻辑 └── logs/ ├── backend.log # 后端运行日志 └── frontend.log # 前端服务日志模块职责非常清晰:main.py只负责 HTTP 层与路由分发,game_logic.py管理游戏状态机,agents.py封装所有 LLM 相关能力,两个工具类继承hello_agents的Tool基类独立完成外部能力接入,前端则是零框架依赖的原生 HTML/CSS/JS。
核心架构:一局游戏的完整流水线
一局游戏的启动并非简单地把名字交给 Agent,而是经历一条**"生成 → 搜索 → 扮演"**的三阶段流水线,全部由HistoricalFigureAgent的构造函数串联(见 agents.py):
- 人物生成:调用
_generate_figure(),由 LLM 随机生成"名称 + 一句话简介"; - 提示预生成:调用
_generate_hints(),先用 Tavily 搜索人物资料,再让 LLM 基于资料产出 3 条由模糊到具体的提示; - 角色扮演 Agent 创建:调用
_create_roleplay_agent(),把人物简介注入 system prompt,创建一个enable_tool_calling=False的纯对话SimpleAgent。
下面逐一深入每一阶段的源码实现。
LLM 动态人物生成:如何避免"每次都是同一个人"
人物生成是整个游戏的随机性来源。项目通过三层机制确保每次开局的人物不重复:
第一层:领域候选池随机采样。agents.py 定义了_FIGURE_DOMAINS,包含 13 个领域:中国古代帝王、文人墨客、军事家、中国神话人物、西游记人物、三国人物、西方历史人物、西方神话人物、世界科学家、知名虚构角色、现代体育明星、中国近现代人物、网络红人与 UP 主。每次开局随机选一个领域作为 system prompt 的方向约束。
第二层:随机种子 + 时间戳注入。_build_random_figure_prompt()(agents.py)会生成一个 10000~99999 的随机种子写入 system prompt;同时 user 消息里还会注入毫秒级时间戳ts = int(time.time() * 1000)与一个 1~9999 的随机数。这种做法一方面是打乱 LLM 缓存的相似请求,另一方面从 prompt 层面强制模型发散。
第三层:严格输出格式解析 + 兜底。prompt 要求模型严格输出两行:
名称:<人物名称> 简介:<一句话概括其性格特点与主要成就,50字以内>_parse_figure()(agents.py)逐行解析,兼容全角/半角冒号与"姓名/名称"两种前缀;一旦解析失败或 LLM 调用异常,则回退到_fallback_figure()内置的人物池(孔子、孙悟空、武则天、诸葛亮、哈利·波特)随机兜底,保证游戏在任何情况下都能开局。
Tavily 搜索增强:由模糊到具体的三条提示
提示(hint)是本游戏的辅助机制,设计上要求"由模糊到具体",第 1 条最模糊、第 3 条最具体,且不能直接说出答案。其实现分两步:
- Tavily 搜索人物资料:
TavilySearchTool(tavily_search_tool.py)继承hello_agents.tools.base.Tool,调用TavilyClient.search(),使用search_depth="basic"、max_results=5、include_answer=False(只取原始检索结果,不生成 AI 摘要)。出于 token 成本与提示质量平衡,工具只取前 1 条结果,并经过正则清洗(去除 URL、折叠空白、去重标点、规整省略号)后截断到 300 字符。 - LLM 生成提示:将搜索资料与答案名一起交给 LLM,由
_HINT_SYSTEM_PROMPT(agents.py)约束"每条提示单独一行、格式为提示N:<内容>、由模糊到具体、不能直接说出答案"。_parse_hints()用正则^(提示\d[::]\s*|\d+[\.、]\s*)清洗行前缀,不足 3 条时用通用 fallback 提示补齐。
值得注意的是,Tavily 未配置时系统不会崩溃:_generate_hints()在self._search_tool为空时直接走_fallback_hints(),返回"这是一个广为人知的事物"等通用提示,实现优雅降级(详见 agents.py)。
沉浸式角色扮演:SimpleAgent 的"人格注入"
角色扮演 Agent 是游戏体验的核心。_create_roleplay_agent()创建了一个name="guess_who_agent"、enable_tool_calling=False的纯对话SimpleAgent——扮演阶段不需要任何工具,避免 Agent 在回答时偏离角色。
其灵魂在于_ROLEPLAY_SYSTEM_PROMPT(agents.py),它要求 Agent:
- 以人物第一人称回答,语气措辞符合性格与时代背景;
- 必须直接针对用户问题给出明确回应("是的"/"不是"/"确实如此"),不能回避或答非所问;
- 在明确回应基础上用符合人物身份的语气补充一句;
- 每次回答 1~2 句话,保持简短;
- 回答基于人物真实生平,不编造;
- 严禁在任何情况下说出人物名称(包括姓名、字号、封号、外号等一切称谓);
- 与人物完全无关的问题用符合身份的方式婉转说明。
"先明确作答 + 再角色化补充"的指令设计,兼顾了游戏可玩性(玩家能获得有效信息)与沉浸感(回答有性格),是角色扮演类 Agent prompt 的实用模板。
语义猜测匹配与画像展示
玩家提交猜测后,HistoricalFigureAgent.make_guess()(agents.py)的处理策略是:
- 精确匹配优先:
guess.strip().lower() == actual_name.lower(); - LLM 语义匹配兜底:精确匹配失败时调用
_semantic_match(),使用_SEMANTIC_MATCH_PROMPT("判断以下两个名称是否指代同一个人物或事物,只需回答'是'或'否'")让 LLM 判断,结果以result.startswith("是")判定。这样"孙悟空 vs 齐天大圣""李太白 vs 李白"这类别名、外号表达也能正确命中。
猜对后,后端调用SearchImageTool.search_photos()(search_image_tool.py)从 Wikipedia 拉取画像。该工具不走容易被 403 的w/api.php搜索接口,而是直接调用 REST Summary API(中文优先、英文兜底,见源码中的_ZH_SUMMARY_URL/_EN_SUMMARY_URL),并伪造浏览器 User-Agent 规避反爬,最终把缩略图与原图 URL 随接口返回,前端据此渲染画像画廊。
游戏状态管理:GameSession 与 GameManager
游戏状态由 game_logic.py 管理:
- GameSession:每局一个实例,持有
session_id(UUID)、current_figure(当前人物)、hints(预生成提示列表)、questions_asked/hints_used(已用提问/提示计数)、is_game_over/is_correct、guess_history(猜测历史),并从配置读取max_questions(10)与max_hints(3)。ask_question():提问计数自增,达到上限即置is_game_over=True;make_guess():先精确匹配、再由注入的semantic_match_fn兜底;猜对或次数用尽即结束游戏并返回figure_info;get_hint():按序返回预生成提示,同时返回hint_level与剩余次数;get_game_status():汇总会话状态供前端刷新。
- GameManager:维护
active_sessions字典,提供create_session/get_session/end_session/cleanup_old_sessions(默认清理超过 60 分钟未更新的会话),并暴露全局单例game_manager。
在 main.py 中,会话存储采用session_id -> (GameSession, HistoricalFigureAgent)的元组配对(active_sessions字典),每次请求通过get_session_pair()校验会话存在性,会话不存在返回 404。
环境要求与配置
环境要求
- Python 3.8+
- ModelScope API Key(必须,提供 OpenAI 兼容的 LLM 接口)
- Tavily API Key(必须,用于搜索增强提示,可在 Tavily 平台注册获取)
依赖清单见 requirements.txt:fastapi==0.104.1、uvicorn[standard]==0.24.0、hello_agents>=0.1.0、python-dotenv==1.0.0、pydantic==2.5.0、httpx==0.25.2、tavily-python>=0.3.0、requests>=2.31.0。
环境变量与配置参数
配置管理在 config.py 中实现:Settings类在模块导入时通过load_dotenv加载backend/.env,并以单例(get_config())方式供全项目读取。环境变量模板见 backend/.env.example。
| 配置项 | 代码默认值 | 说明 |
|---|---|---|
LLM_MODEL_ID | qwen-flash | 使用的 LLM 模型(推荐 flash 系列以降低延迟) |
LLM_API_KEY | 空 | ModelScope API Key(必填) |
LLM_BASE_URL | https://api-inference.modelscope.cn/v1/ | LLM 接口地址(OpenAI 兼容) |
LLM_TIMEOUT | 30(秒) | LLM 请求超时;.env.example示例值给出180以应对长响应 |
TAVILY_API_KEY | 空 | Tavily 搜索 Key(必填),未配置时自动降级为 fallback 提示 |
MAX_QUESTIONS | 10 | 每局最大提问次数(代码级常量,不读 .env) |
MAX_HINTS | 3 | 每局最大提示次数(代码级常量,不读 .env) |
HOST/PORT | 0.0.0.0/8000 | 后端监听地址与端口(代码级常量) |
需要注意:MAX_QUESTIONS、MAX_HINTS、HOST、PORT属于"代码级默认值,不存放在 .env"(见 config.py 注释),如需调整需直接修改源码。Settings.validate()会在启动时检查LLM_API_KEY是否为空并打印警告。
快速开始
1. 安装依赖
cd /path/to/repo/Co-creation-projects/afei-GuessWhoAmI/backend pip install -r requirements.txt2. 配置环境变量
复制模板并填写配置:
cp backend/.env.example backend/.env编辑backend/.env:
# LLM 配置(ModelScope API,必填) LLM_MODEL_ID=qwen-flash LLM_API_KEY=your_modelscope_api_key LLM_BASE_URL=https://api-inference.modelscope.cn/v1/ LLM_TIMEOUT=180 # Tavily 搜索 API(必填,用于搜索增强提示) TAVILY_API_KEY=your_tavily_api_key3. 一键启动(推荐)
使用 restart.sh 同时启动前后端:
cd /path/to/repo/Co-creation-projects/afei-GuessWhoAmI bash restart.sh脚本会自动:
- 停止已有的前后端进程(按端口
lsof清理 + 按进程名pgrep清理); - 启动后端(FastAPI,端口8000);
- 启动前端(Python
http.server,端口3000); - 通过
wait_for_port轮询等待服务就绪(最长 15 秒),超时报错并提示查看日志。
启动成功后输出示例:
✅ All services started successfully! 🔧 Backend → http://localhost:8000 🔧 API Docs → http://localhost:8000/docs 🌐 Frontend → http://localhost:3000⚠️ 注意:restart.sh中硬编码了 Python 解释器路径VENV_PYTHON="/home/afei/hello_agent_venv/bin/python"(脚本第 14 行),这是作者机器上的虚拟环境路径,其他环境运行前需将其改为本机 Python 路径。
4. 访问地址
| 服务 | 地址 |
|---|---|
| 🌐 游戏前端 | http://localhost:3000 |
| 🔧 后端 API | http://localhost:8000 |
| 📖 API 文档 | http://localhost:8000/docs |
5. 手动启动(可选)
# 启动后端 cd backend python main.py # 启动前端(另开终端) cd frontend python -m http.server 3000API 接口一览
后端基于 FastAPI 提供 RESTful 接口(路由定义见 main.py):
| 方法 | 路径 | 说明 |
|---|---|---|
POST | /api/game/start | 开始新游戏(LLM 生成人物 + 预生成提示) |
POST | /api/game/chat | 向 Agent 提问(角色扮演对话) |
POST | /api/game/guess | 提交猜测(语义匹配判断,猜对返回人物图片) |
GET | /api/game/hint | 获取下一条提示 |
POST | /api/game/end | 结束当前游戏 |
GET | /api/game/status/{session_id} | 获取当前游戏状态 |
所有接口统一返回 models.py 中定义的GameResponse结构:
class GameResponse(BaseModel): success: bool message: str data: Optional[dict] = None error: Optional[str] = None几个实现细节值得注意:
- start接口在每次开局前调用
_clear_log_file()截断后端日志文件——由于日志由 Python 的FileHandler持有文件描述符,代码采用"关闭流 → 截断文件 → 重新打开追加流"的方式安全清空,避免写入 NUL 字节(见 main.py); - chat接口在游戏结束后(
is_game_over=True)会拒绝消息并提示"请开始新游戏"; - end接口返回完整状态(含答案
figure_name)并从active_sessions中移除会话; - CORS中间件配置为
allow_origins=["*"],允许所有来源跨域访问,方便前端本地联调。
前端交互与游戏规则
前端是零框架的原生实现:index.html定义页面结构(开始页/游戏页/结果弹窗),style.css负责样式,app.js 用Fetch API与后端通信。核心交互逻辑:
startNewGame():POST/api/game/start,把返回的session_id、max_questions、max_hints存入前端状态,切换到游戏界面并展示欢迎语;sendMessage():POST/api/game/chat,将用户输入渲染为气泡并展示 Agent 回复,随后用服务端返回的remaining_questions刷新统计;requestHint():POST/api/game/hint,展示💡 提示:...并更新剩余提示数;submitGuess():POST/api/game/guess,猜对时调用endGame(true, figure_info, portrait_images)渲染画像画廊,猜错则继续并同步剩余提问数。
完整游戏规则如下:
- 点击「开始游戏」,系统由 LLM 随机生成一位人物(历史、神话、虚构、网络红人均有可能);
- 通过对话向 Agent 提问,Agent 以该人物第一人称回答,不会直接说出名字;
- 最多可提问10 次,可使用提示3 次(提示由模糊到具体);
- 随时可以提交猜测,支持别名、外号等多种表达方式;
- 猜对后展示人物图片;提问次数用完或主动结束则游戏结束并揭晓答案。
技术栈
后端
- FastAPI—— Web 框架(含自动生成
/docs交互式 API 文档) - hello_agents—— AI Agent 框架(
SimpleAgent、HelloAgentsLLM、Message、Tool基类) - Pydantic v2—— 数据验证
- Uvicorn—— ASGI 服务器
- Tavily Python SDK—— 搜索增强
- Wikipedia REST API—— 人物图片搜索
前端
- HTML5 / CSS3 / JavaScript—— 原生实现,无框架依赖
- Fetch API—— 与后端通信
AI / LLM
- ModelScope API—— OpenAI 兼容接口(默认模型:
qwen-flash) - LLM 人物生成—— 动态随机生成,避免重复
- LLM 语义匹配—— 判断猜测是否与答案指代同一人物
日志与故障排除
日志
运行日志保存在logs/目录:
# 实时查看后端日志 tail -f logs/backend.log # 实时查看前端日志 tail -f logs/frontend.log后端日志采用"自定义FileHandler挂到 root logger"的方式写入(刻意不用basicConfig,因为它对已被 uvicorn 预配置过 handler 的 root logger 是空操作),格式为时间 [级别] 日志名 - 消息,并在 start 接口中做文件截断清理,相关实现见 main.py。
故障排除
LLM 调用失败
- 检查
backend/.env中的LLM_API_KEY和LLM_BASE_URL; - 确认 ModelScope 账号有对应模型的访问权限;
- 代码中 LLM 调用异常均有兜底(人物生成走内置人物池、对话返回"抱歉,我现在有些恍惚,请再问一次吧。"),游戏不会因单次调用失败而崩溃。
每次生成同一个人物
- 已通过随机种子 + 时间戳注入解决;若仍出现,请检查 LLM 模型是否支持随机性参数。
Tavily 搜索不可用
- 检查
backend/.env中的TAVILY_API_KEY是否正确填写; - 未配置时系统会自动降级使用 fallback 提示,但提示质量会下降(从源码可见
_generate_hints()中 Tavily 与 LLM 任一环节失败都会走 fallback)。
端口被占用
restart.sh会自动清理占用端口的进程(kill_port与kill_pattern),重新运行脚本即可。
CORS 错误
- 后端已配置 CORS 允许所有来源,确保前端访问正确的后端端口(默认 8000)。
总结与可扩展方向
GuessWhoAmI 用不到千行代码,完整示范了"LLM 随机内容生成 + 外部搜索工具增强 + 无工具角色扮演 + LLM 语义判断 + 图像检索"五类 Agent 能力的组合方式,且每一环都有降级兜底设计,工程上相当完整。在此基础上还可以继续扩展:
- 增加难度分级:把人物领域、提问次数、提示数量做成可配置的难度档位;
- 多人对战:利用
GameManager.cleanup_old_sessions的会话清理机制,扩展为多房间同时开局的在线对战模式; - 接入更多搜索/图像源:
TavilySearchTool与SearchImageTool均继承自hello_agents的Tool基类,只需实现run()与get_parameters()即可低成本替换或新增工具; - 引入语音/多模态:结合 LLM 多模态能力,让 Agent 用语音作答或支持图片式谜题。
对想要上手hello_agents框架的开发者而言,这个项目是"从框架 API 到完整可玩游戏"的最短路径之一,值得通读其 agents.py 与 game_logic.py 后自行复刻一版。
【免费下载链接】hello-agents📚 《从零开始构建智能体》——从零开始的智能体原理与实践教程项目地址: https://gitcode.com/GitHub_Trending/he/hello-agents
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考