用 HelloAgents 与 Godot 构建赛博小镇:从零打造具有记忆与好感度的 AI NPC 游戏
【免费下载链接】hello-agents📚 《从零开始构建智能体》——从零开始的智能体原理与实践教程项目地址: https://gitcode.com/GitHub_Trending/he/hello-agents
本篇技术指南以《从零开始构建智能体》第十五章为骨架,完整讲解如何将 HelloAgents 智能体框架与 Godot 游戏引擎结合,构建一个包含智能 NPC 对话、短期/长期记忆、五级好感度、批量对话生成与实时日志的 2D 像素风 AI 小镇。读者将掌握"游戏引擎 + 后端服务"分层架构的搭建方法,理解 SimpleAgent 如何承载 NPC 智能,并能独立复现或扩展一个真正"活着"的 AI NPC 系统。
1 项目概述与架构设计
1.1 为什么要构建 AI 小镇
传统游戏中的 NPC 通常只能说出固定台词,或通过预设对话树进行有限互动。即使是最复杂的 RPG,NPC 对话也由编剧事先写死——这种方式虽然可控,但缺乏真正的"智能"和"生命力"。
如果游戏中的 NPC 能够理解玩家说的任何话、记住上次聊了什么、记得你们的关系,甚至记住玩家的喜好,会怎样?每个 NPC 都有自己的职业、性格和说话风格,对玩家的态度会随互动变化,从陌生人到朋友,再到挚友。这正是大语言模型与游戏引擎结合带来的新可能,它并非单纯的技术演示,而是对未来游戏形态的探索:
- 教育游戏:NPC 扮演历史人物、科学家,与学生进行互动式教学;
- 虚拟办公室:NPC 扮演同事、导师,提供帮助和建议;
- 陪伴场景:NPC 作为陪伴者进行情感交流,可应用于心理健康领域;
- 传统游戏增强:为既有游戏加入 AI NPC,提升玩家体验。
1.2 技术架构概览:游戏引擎 + 后端服务
赛博小镇采用游戏引擎 + 后端服务的分离架构,分为四个层次:
| 层次 | 技术选型 | 职责 |
|---|---|---|
| 前端层 | Godot 4.5 游戏引擎 | 游戏渲染、玩家控制、NPC 显示、对话 UI |
| 后端层 | FastAPI 框架 | API 路由、NPC 状态管理、对话处理、日志记录 |
| 智能体层 | HelloAgents 框架 | NPC 智能、记忆管理、好感度计算(每个 NPC 是一个 SimpleAgent 实例) |
| 外部服务层 | LLM API、Qdrant、SQLite | 大模型能力、向量存储、数据持久化 |
数据流转闭环如下:玩家在 Godot 中按 E 键与 NPC 互动 → Godot 通过 HTTP API 发送对话请求到 FastAPI 后端 → 后端调用 HelloAgents 的 SimpleAgent 处理对话 → Agent 从记忆系统检索相关历史 → 调用 LLM 生成回复 → 后端更新 NPC 状态和好感度、记录日志 → 返回回复给 Godot 前端 → Godot 显示回复并更新 UI,完成一次完整交互循环。
项目源码位于 code/chapter15/Helloagents-AI-Town,其结构如下:
Helloagents-AI-Town/ ├── helloagents-ai-town/ # Godot游戏项目 │ ├── project.godot # Godot项目配置 │ ├── scenes/ # 游戏场景 (main/player/npc/dialogue_ui .tscn) │ ├── scripts/ # GDScript脚本 (main/player/npc/dialogue_ui/api_client/config .gd) │ └── assets/ # 游戏资源 (characters/interiors/ui/audio) └── backend/ # Python后端 ├── main.py # FastAPI主程序 ├── agents.py # NPC Agent系统 ├── relationship_manager.py # 好感度管理 ├── state_manager.py # 状态管理 ├── batch_generator.py # 批量对话生成器 ├── logger.py # 日志系统 ├── config.py # 配置管理 ├── models.py # 数据模型 ├── requirements.txt # Python依赖 └── view_logs.py # 日志查看工具1.3 快速体验:5 分钟运行项目
环境要求:Godot 4.2 或更高版本、Python 3.10 或更高版本、LLM API 密钥。
启动后端:
# 1. 进入backend目录 cd Helloagents-AI-Town/backend # 2. 安装依赖 pip install -r requirements.txt # 3. 配置环境变量 cp .env.example .env # 编辑.env文件,填写你的API密钥 # 4. 启动后端服务 python main.py实际仓库的依赖清单(见 backend/requirements.txt)包括fastapi>=0.104.0、uvicorn[standard]>=0.24.0、pydantic>=2.0.0、python-dotenv,以及 HelloAgents 框架依赖hello-agents>=0.2.4,<=0.2.9。
关于 LLM 配置,需要说明的是:实际仓库的 config.py 并不直接读取OPENAI_API_KEY,而是通过三个环境变量驱动,且默认接入 ModelScope(魔搭)推理服务:
| 环境变量 | 默认值 | 说明 |
|---|---|---|
LLM_MODEL_ID | Qwen/Qwen2.5-72B-Instruct | 使用的模型 |
LLM_API_KEY | 无(未设置时启动会告警) | 服务密钥 |
LLM_BASE_URL | https://api-inference.modelscope.cn/v1/ | API 服务地址 |
启动时会先执行settings.validate()检查密钥,若未配置LLM_API_KEY会打印告警,但服务仍可启动——此时 NPC Agent 进入模拟模式,返回"请配置 API_KEY 以启用 AI 对话"的提示,方便无密钥时先联调前后端流程。
成功启动后输出如下:
============================================================ 🎮 赛博小镇后端服务启动中... ============================================================ ✅ 所有服务已启动! 📡 API地址: http://0.0.0.0:8000 📚 API文档: http://0.0.0.0:8000/docs ============================================================启动 Godot:从官网下载对应平台的安装包(Windows 为.exe,macOS 为.dmg)。打开 Godot 引擎,点击"导入",浏览到Helloagents-AI-Town/helloagents-ai-town/scenes/main.tscn,点击"导入并编辑"。等待资源导入后按F5或点击"运行"启动游戏。
体验核心功能:使用 WASD 移动玩家角色,走近 NPC 时屏幕显示"按 E 键交互"提示;按 E 键弹出对话框,可输入任意内容。NPC 会根据角色设定(Python 工程师、产品经理、UI 设计师)和互动历史做出回应。随着对话推进,好感度会从"陌生"逐步提升到"挚友"。
好感度系统在后端实现,虽然前端不直接显示数值,但所有变化都被记录到backend/logs/dialogue_YYYY-MM-DD.log中,包括:当前好感度值、检索到的相关记忆、NPC 的回复、好感度变化量(+2.0、+3.0 等)、变化原因(友好问候、正常交流等)与情感分析结果(positive、neutral 等)。可在 backend 目录下运行以下命令实时查看日志:
python view_logs.py2 NPC 智能体系统
2.1 基于 HelloAgents 的 SimpleAgent
在赛博小镇中,每个 NPC 都是独立的智能体。我们使用 HelloAgents 框架中的SimpleAgent——一个轻量级智能体实现,封装了 LLM 调用、消息管理和工具调用等核心功能。其核心是一个简单的对话循环:接收用户消息 → 调用 LLM 生成回复 → 返回结果。
为每个 NPC 创建独立 Agent 的流程是:定义 NPC 基本信息(ID、名称、职业、性格)→ 据此构建系统提示词让 LLM 扮演角色 → 创建 SimpleAgent 实例并配置记忆系统。文档中的教学版示例代码如下:
from hello_agents import SimpleAgent, HelloAgentsLLM from hello_agents.memory import MemoryManager, WorkingMemory, EpisodicMemory def create_npc_agent(npc_id: str, name: str, role: str, personality: str): """创建NPC Agent""" system_prompt = f"""你是{name},一位{role}。 你的性格特点:{personality} 你在Datawhale办公室工作,与同事们一起推动开源社区的发展。 请根据你的角色和性格,自然地与玩家对话。 记住你们之前的对话内容,保持对话的连贯性。 """ llm = HelloAgentsLLM() memory_manager = MemoryManager( working_memory=WorkingMemory(capacity=10, ttl_minutes=120), episodic_memory=EpisodicMemory( db_path=f"memory_data/{npc_id}_episodic.db", collection_name=f"{npc_id}_memories" ) ) agent = SimpleAgent( name=name, llm=llm, system_prompt=system_prompt, memory_manager=memory_manager ) return agent这里 WorkingMemory 是短期记忆,容量 10 条消息、保留 120 分钟;EpisodicMemory 是长期记忆,基于 SQLite 与 Qdrant 向量库存储并支持语义检索。
仓库实际实现(backend/agents.py)在此基础上做了进一步工程化:NPCAgentManager在初始化时为每个 NPC 创建MemoryManager,并为其配置更精细的 MemoryConfig:
memory_config = MemoryConfig( storage_path=memory_dir, # 每个NPC独立的存储目录 working_memory_capacity=10, # 工作记忆:最近10条对话 working_memory_tokens=2000, # 工作记忆:最多2000个token max_capacity=100, # 长期记忆:最多100条 importance_threshold=0.3, # 检索时只关注重要性>=0.3的记忆 decay_factor=0.95 # 时间衰减系数 ) memory_manager = MemoryManager( config=memory_config, user_id=npc_name, # 用NPC名字作为user_id enable_working=True, # 启用工作记忆(短期) enable_episodic=True, # 启用情景记忆(长期) enable_semantic=False, # 不需要语义记忆 enable_perceptual=False # 不需要感知记忆 )同时,仓库实现为"每个 NPC 一个记忆目录"的持久化方案,运行后会在backend/memory_data/张三/、李四/、王五/下分别生成memory.db,实现 NPC 之间记忆隔离。
2.2 NPC 角色设定与 Prompt 设计
赛博小镇设计了三个性格鲜明的 NPC。仓库实际定义(backend/agents.py#L21-L49)比文档示例更完整,每个 NPC 包含职位、位置、活动、性格、专长、说话风格、爱好七个维度:
| NPC | 职位 | 位置 | 当前活动 | 性格 | 专长 |
|---|---|---|---|---|---|
| 张三 | Python工程师 | 工位区 | 写代码 | 技术宅,喜欢讨论算法和框架 | 多智能体系统、HelloAgents框架、Python开发、代码优化 |
| 李四 | 产品经理 | 会议室 | 整理需求 | 外向健谈,善于沟通协调 | 需求分析、产品规划、用户体验、项目管理 |
| 王五 | UI设计师 | 休息区 | 喝咖啡 | 细腻敏感,注重美感 | 界面设计、交互设计、视觉呈现、用户体验 |
对应文档中的简化版配置如下:
npc_zhang = { "npc_id": "zhang_san", "name": "张三", "role": "Python工程师", "personality": "严谨、专业、喜欢分享技术知识。说话直接,注重代码质量。" } npc_li = { "npc_id": "li_si", "name": "李四", "role": "产品经理", "personality": "外向、善于沟通、注重用户体验。喜欢从用户角度思考问题。" } npc_wang = { "npc_id": "wang_wu", "name": "王五", "role": "UI设计师", "personality": "温和、富有创意、审美独特。注重视觉呈现和用户体验。" }仓库中的create_system_prompt()会基于这些配置生成结构化提示词,包含角色设定、行为准则(如"保持角色一致性,用第一人称'我'回答""回复控制在 30-50 字以内""不要说自己'是AI/语言模型'")、以及对话示例(few-shot),让 LLM 扮演的角色更稳定、更像真实的办公室同事。
2.3 记忆系统集成
记忆系统是 NPC 智能的关键。两层记忆分工明确:
- 短期记忆(WorkingMemory):存储最近对话,容量有限、随时间自动清理,作用是保持对话连贯性——当玩家说"它是什么颜色的?"时,NPC 需要从短期记忆中找出"它"指什么;
- 长期记忆(EpisodicMemory):存储全部对话历史,使用向量库做语义检索——当玩家说"还记得我们上次讨论的那个项目吗?"时,NPC 能检索到相关历史。
实际对话处理时,Agent 先取短期记忆最近对话,再从长期记忆检索相关历史,拼装上下文后交给 LLM。文档中的核心流程如下:
def process_dialogue(agent, player_message): # 1. 从短期记忆获取最近对话 recent_messages = agent.memory_manager.working_memory.get_recent_messages(5) # 2. 从长期记忆检索相关历史 relevant_memories = agent.memory_manager.episodic_memory.search( query=player_message, top_k=3 ) # 3. 构建上下文 context = {"recent": recent_messages, "relevant": relevant_memories} # 4. 调用Agent生成回复 reply = agent.run(player_message, context=context) # 5. 保存到记忆系统 agent.memory_manager.add_interaction(player_message, reply) return reply仓库实现(NPCAgentManager.chat())在 agents.py 中把"检索-增强-生成-记忆-好感度"串成了完整流水线:
- 获取当前好感度并生成关系上下文(等级 + 对话风格修饰词);
- 调用
memory_manager.retrieve_memories(query=message, memory_types=["working", "episodic"], limit=5, min_importance=0.3)检索相关记忆; - 构建增强提示词:
【当前关系】+【之前的对话记忆】+【当前对话】三段拼接后交给agent.run(); - 调用好感度管理器分析并更新好感度;
- 将"玩家说:…"与"我说:…"两条消息连同好感度、情感倾向等元数据写入记忆(见
_save_conversation_to_memory())。
值得注意的是,仓库还会把当时的affinity、affinity_change、sentiment写入记忆的metadata,即好感度信息本身也参与记忆存储,为后续按情感/关系维度召回对话提供了数据基础。
2.4 批量对话生成:轻负载模式
当多个玩家同时与不同 NPC 对话时,后端需并发处理多个 LLM 请求,成本与延迟都会上升。为此设计了批量对话生成系统:将多个 NPC 的对话请求合并成一次 LLM 调用,让 LLM 一次性生成所有 NPC 的回复——如同餐厅"预制菜",一次 API 调用成本可降至原来的约 1/3,延迟大幅下降。
仓库实现位于 backend/batch_generator.py:
class NPCBatchGenerator: def __init__(self): self.llm = HelloAgentsLLM() self.npc_configs = NPC_ROLES # 所有NPC的配置 def generate_batch_dialogues(self, context=None) -> Dict[str, str]: prompt = self._build_batch_prompt(context) response = self.llm.invoke([ {"role": "system", "content": "你是一个游戏NPC对话生成器,擅长创作自然真实的办公室对话。"}, {"role": "user", "content": prompt} ]) dialogues = json.loads(response) # {"张三": "...", "李四": "...", "王五": "..."} return dialogues批量生成提示词的关键是严格约束输出格式:要求每个 NPC 生成 1 句话(20-40 字)、内容符合角色与场景氛围,且必须严格按照 JSON 格式返回,并给出示例输出:
{"张三": "这个bug真是见鬼了,已经调试两小时了...", "李四": "嗯,这个功能的优先级需要重新评估一下。", "王五": "这杯咖啡的拉花真不错,灵感来了!"}由于所有 NPC 的对话在同一个上下文中生成,彼此天然存在关联性:张三调试 bug 时李四可能提到帮忙看看,王五设计界面时张三可能说等会儿去看设计稿,办公室氛围更真实连贯。
工程细节:仓库为批量生成器设计了优雅降级路径。_get_current_context()根据当前小时自动推断场景(清晨/上午工作/午餐/下午工作/傍晚/夜晚);当 LLM 不可用时,回退到按时间段(morning/noon/afternoon/evening)选取的预设对话库,保证 NPC 状态接口始终可用。_parse_response()则依次尝试直接 JSON 解析、截取首尾花括号提取、失败后回退预设对话,鲁棒性较好。
批量生成更适合 NPC 的"背景对话"或"自言自语",主要应用于:NPC 背景对话(玩家进入场景时 NPC 正在做什么)、定时更新、按时间生成场景氛围、以及高并发下降成本。
混合模式:批量生成 + 即时响应:系统后台定期批量生成所有 NPC 的"背景对话"并缓存,玩家靠近 NPC 但未交互时,NPC 显示"正在调试代码…""在看产品文档…"等背景对话,看起来是"活着的";玩家按下 E 键发起交互时立即切换到即时响应,调用该 NPC 的专属 Agent,基于具体消息、历史记忆与好感度生成个性化回复。仓库中由 state_manager.py 的NPCStateManager承载:默认每 30 秒(NPC_UPDATE_INTERVAL)触发一次_update_npc_states()批量生成并缓存,同时提供force_update()支持前端手动触发刷新(对应/npcs/status/refresh接口)。
这种混合模式的优势:背景对话批量生成成本低;玩家交互即时响应质量高;NPC 始终有背景对话显得生动;批量生成频率可根据服务器负载动态调整。
3 好感度系统设计
3.1 好感度等级划分
好感度系统通过量化 NPC 与玩家的关系,让回复更真实、有层次感。系统划分为五个等级,每个等级对应分数范围和行为表现:
| 等级 | 分数范围 | NPC 行为表现 |
|---|---|---|
| 陌生 | 0-20 分 | 礼貌但保持距离,回复简短,不主动分享个人信息 |
| 熟悉 | 21-40 分 | 开始记住玩家,愿意简单交流,偶尔分享工作信息 |
| 友好 | 41-60 分 | 把玩家当朋友,回复更详细,主动询问玩家情况 |
| 亲密 | 61-80 分 | 非常信任玩家,愿意分享私人话题,提供帮助和建议 |
| 挚友 | 81-100 分 | 把玩家当最好的朋友,无话不谈,分享内心想法 |
这种设计让玩家能清晰感知关系变化,也为玩法扩展提供基础——比如只有达到一定好感度,NPC 才分享特殊信息或提供特殊任务。
3.2 好感度计算逻辑
好感度不能简单固定加分,否则会显得机械。赛博小镇使用LLM 分析对话内容,自动判断玩家态度是友好、中立还是不友好,再动态调整分数,全程无需玩家刻意选择选项。
文档中的教学版RelationshipManager通过一个简单 prompt 让 LLM 返回数字分数:
class RelationshipManager: def analyze_sentiment(self, player_message: str, npc_reply: str) -> int: prompt = f"""分析以下对话中玩家的态度: 玩家: {player_message} NPC: {npc_reply} 请判断玩家的态度是: 1. 友好(+5分): 礼貌、热情、表示感谢或赞同 2. 中立(+2分): 普通的询问或陈述 3. 不友好(-3分): 粗鲁、冷漠、批评或否定 只返回数字,不要其他内容。""" response = self.llm.think([{"role": "user", "content": prompt}]) try: score_change = int(response.strip()) return max(-3, min(5, score_change)) # 限制在-3到5之间 except: return 2 # 默认中立仓库实际实现(backend/relationship_manager.py)将"情感分析"升级为独立的SimpleAgent(名为AffinityAnalyzer),从四个维度分析并输出结构化 JSON:
{ "should_change": true, "change_amount": -15到+10之间的整数, "reason": "简短说明原因(10字以内)", "sentiment": "positive/neutral/negative" }好感度变化规则在分析 Agent 的系统提示词中明确定义:赞美/感谢/请教 +3 到 +8;友好问候/正常交流 +1 到 +3;普通闲聊 0;批评/质疑/不耐烦 -3 到 -8;侮辱/攻击/恶意 -8 到 -15。更新时分数被钳制在 0-100 之间,等级由get_affinity_level()判定。为提升鲁棒性,_parse_analysis()对 LLM 输出做了三重解析兜底:直接json.loads→ 截取首尾花括号再解析 → 正则提取字段,均失败则返回默认值(should_change=False),避免一次解析异常影响整个对话流程。
3.3 好感度影响对话
好感度不是孤立数字,它会真正改变 NPC 的行为——通过动态调整系统提示词实现。文档中给出了按等级映射对话风格的方案:
affinity_prompts = { "陌生": "你刚认识这位玩家,保持礼貌但不要过于热情。回复简短专业。", "熟悉": "你已经认识这位玩家,可以进行正常的交流。回复自然友好。", "友好": "你把这位玩家当作朋友,愿意分享更多信息。回复详细热情。", "亲密": "你非常信任这位玩家,可以分享私人话题。回复充满关心。", "挚友": "你把这位玩家当作最好的朋友,无话不谈。回复亲切真诚。" }系统提示词中会注入当前与玩家的关系:{affinity_level}及对应风格描述。仓库中这一机制由get_affinity_modifier()实现,并在NPCAgentManager.chat()中把关系上下文拼接到增强提示词的【当前关系】段落——好感度低时 NPC"冷淡疏离,回答简短",高时"非常热情友好,像老朋友一样亲切"。玩家可以明显感受到态度随互动逐渐变化,沉浸感与趣味性大增。
4 后端服务实现
4.1 FastAPI 应用结构
后端使用 FastAPI 构建,采用模块化设计,将不同功能分离到独立文件中。入口文件 backend/main.py定义应用与生命周期管理:
@asynccontextmanager async def lifespan(app: FastAPI): # 启动时:验证配置 -> 初始化NPC管理器 -> 启动状态管理器后台任务 settings.validate() npc_manager = get_npc_manager() state_manager = get_state_manager(settings.NPC_UPDATE_INTERVAL) await state_manager.start() yield # 关闭时:停止后台任务 await state_manager.stop() app = FastAPI( title=settings.API_TITLE, version=settings.API_VERSION, description="赛博小镇 - 基于HelloAgents的AI NPC对话系统", lifespan=lifespan ) app.add_middleware( CORSMiddleware, allow_origins=settings.CORS_ORIGINS, # 默认["*"],生产环境应限制具体域名 allow_credentials=True, allow_methods=["*"], allow_headers=["*"], )注意实际仓库使用了 FastAPI 推荐的lifespan异步上下文管理器(而非文档示例中的@app.on_event("startup")),这是 FastAPI 新版本的推荐写法。
4.2 API 路由设计
实际仓库的 API 端点与文档教学示例略有差异(文档示例为/dialogue,仓库实现为/chat),以仓库为准:
| 方法 | 路径 | 功能 |
|---|---|---|
| GET | / | 服务信息与端点索引 |
| GET | /health | 健康检查 |
| POST | /chat | 与 NPC 实时对话(即时响应,走独立 Agent) |
| GET | /npcs | 获取所有 NPC 列表 |
| GET | /npcs/status | 获取批量生成的 NPC 背景对话 |
| POST | /npcs/status/refresh | 强制刷新 NPC 背景对话 |
| GET | /npcs/{npc_name} | 获取单个 NPC 详情(含当前背景对话) |
| GET/DELETE | /npcs/{npc_name}/memories | 查看/清空 NPC 记忆(后者用于测试) |
| GET/PUT | /npcs/{npc_name}/affinity | 查看/设置好感度(后者用于测试) |
| GET | /affinities | 获取所有 NPC 好感度 |
核心的对话接口逻辑(main.py#L103-L134):
@app.post("/chat", response_model=ChatResponse) async def chat_with_npc(request: ChatRequest): npc_mgr, _ = get_managers() npc_info = npc_mgr.get_npc_info(request.npc_name) if not npc_info: raise HTTPException(status_code=404, detail=f"NPC '{request.npc_name}' 不存在") try: response_text = npc_mgr.chat(request.npc_name, request.message) return ChatResponse( npc_name=request.npc_name, npc_title=npc_info["title"], message=response_text, success=True ) except Exception as e: raise HTTPException(status_code=500, detail=f"对话处理失败: {str(e)}")请求/响应模型使用 Pydantic 定义(backend/models.py),例如ChatRequest要求npc_name与message两个必填字段,ChatResponse返回 NPC 名称、职位、回复内容与成功标记。
文档示例还展示了完整的对话流程——验证 NPC 存在 → 检查是否忙碌(409 冲突)→ 标记忙碌 → 获取好感度 → 调用 Agent 生成回复 → 更新好感度 → 记录日志 → 返回响应 → finally 释放状态——这套"忙碌互斥 + 事务式处理"的流程设计在并发场景下仍有参考价值。
4.3 状态管理与日志系统
状态管理器负责跟踪每个 NPC 的位置、忙碌状态、当前动作等,防止并发问题(如一个 NPC 同时与多个玩家对话)。文档示例用内存字典记录is_busy、current_action、last_interaction等字段;实际仓库的 state_manager.py 则聚焦"定时批量生成背景对话 + 状态缓存",用asyncio后台任务每 30 秒执行一次批量更新,对外提供get_current_state()(含对话内容、上次更新时间、下次更新倒计时)与force_update()。
日志系统实现控制台 + 文件双输出。仓库版 logger.py 将每次对话拆分为多个语义化步骤(对话开始、当前好感度、记忆检索条数、生成回复、NPC 回复、分析好感度、好感度变化及原因/情感、记忆保存、对话结束),日志文件按日期命名dialogue_YYYY-MM-DD.log,方便开发者完整追踪"玩家消息 → 记忆召回 → 回复 → 关系变化"的链路:
💬 对话开始: 张三 <-> 玩家 📝 玩家消息: 你好! 💖 当前好感度: 50.0/100 (熟悉) 🧠 检索到2条相关记忆 🤖 正在生成回复... 💬 张三回复: 你好!我是张三,一名Python工程师... 📊 正在分析好感度变化... 📈 好感度变化: 50.0 -> 52.0 (+2.0) 原因: 友好问候 情感: positive 💾 对话已保存到张三的记忆中 ✅ 对话完成4.4 理解 Godot 的场景系统
节点(Node)是 Godot 最基本的构建块,可理解为"乐高积木",每种节点专注做好一件事:Sprite2D显示图片、AudioStreamPlayer播放音频、CharacterBody2D处理角色物理移动。节点可构成父子树状结构,移动/隐藏父节点会连带影响所有子节点,便于组织复杂游戏对象。
场景(Scene)是节点的集合,保存为.tscn文件,相当于"预制件"。场景强大之处在于可复用与模块化:可在场景内实例化另一场景形成嵌套,修改 NPC 场景会自动影响所有 NPC 实例。
例如一个"玩家"场景的树状结构:
Player (CharacterBody2D) ← 根节点,负责物理移动 ├─ AnimatedSprite2D ← 子节点,显示角色动画 ├─ CollisionShape2D ← 子节点,定义碰撞形状 └─ Camera2D ← 子节点,摄像机跟随玩家赛博小镇中三个 NPC(张三、李四、王五)都是同一个NPC.tscn的实例,仅通过脚本参数设置不同名称和角色信息。若想给所有 NPC 增加新功能(如头顶对话气泡),只需修改 NPC 场景一处。
5 Godot 游戏场景构建
5.1 为什么选择 Godot
选择 Godot 4.5 作为前端引擎,主要基于四点考虑:
- 2D 开发天然优势:赛博小镇是俯视角 2D 像素风格游戏,Godot 提供
TileMap、AnimatedSprite2D、CharacterBody2D等专为 2D 设计的节点,开发效率高;场景系统可将玩家、NPC、UI 封装为独立场景再实例化,契合组件化需求; - 完全开源免费:MIT 许可证,无版权费用或收入分成,可自由修改引擎源码,对教学与开源项目非常友好;
- 学习成本极低:GDScript 是类似 Python 的动态类型语言,变量声明、函数定义、控制流程与 Python 高度相似,熟悉 Python 的读者几小时内即可上手;节点树结构在编辑器中直观可见;
- 与 Python 后端集成简单:内置
HTTPRequest节点可轻松与 FastAPI 进行 HTTP 通信,前后端分离的架构可独立开发和测试游戏逻辑与 AI 逻辑。
局限性:Godot 的 3D 能力相比 Unreal/Unity 尚有差距,大型 3D 游戏需考虑其他引擎;但对于 2D 游戏、独立游戏与教学项目,Godot 是优秀选择。
5.2 场景设计与资源组织
游戏由四个核心场景组成:Main(主场景)、Player(玩家)、NPC(非玩家角色)、DialogueUI(对话界面)。三个 NPC 是同一个 NPC 场景的实例,只是通过脚本参数设置不同的角色信息。四个场景在 Godot 中的创建步骤:
- Player 场景:新建场景,选
CharacterBody2D为根节点,添加AnimatedSprite2D、CollisionShape2D、Camera2D、InteractSound、RunningSound子节点,保存为Player.tscn; - NPC 场景:根节点
CharacterBody2D,添加CollisionShape2D、AnimatedSprite2D、InteractionArea(Area2D,下含 CollisionShape2D)、NameLabel、DialogueLabel,保存为NPC.tscn; - DialogueUI 场景:根节点
CanvasLayer,添加Panel,其下放NPCName、NPCTitle、DialogueText(RichTextLabel)、PlayerInput(LineEdit)、SendButton、CloseButton,保存为DialogueUI.tscn; - Main 场景:根节点
Node2D,添加Background(Sprite2D 背景图与小鲸鱼装饰)、实例化 Player 场景、创建NPCs节点并在其下三次实例化 NPC 场景、实例化 DialogueUI 场景、创建Walls节点组织墙体碰撞、添加AudioStreamPlayer播放背景音乐。
5.3 玩家控制实现
玩家场景的核心是CharacterBody2D根节点(物理移动与碰撞)+AnimatedSprite2D(动画)+CollisionShape2D(碰撞形状)+Camera2D(跟随)+ 两个AudioStreamPlayer(交互/走路音效)。player.gd的关键机制:
_ready()中调用add_to_group("player"),NPC 通过这个组识别玩家;_physics_process()用Input.get_vector()读取方向,move_and_slide()移动;交互状态下velocity置零禁用移动;update_animation()按移动方向播放 4 方向动画(walk_up/down/left/right),并处理flip_h镜像;静止时播放idle;_input()监听 E 键或回车触发interact_with_npc(),播放交互音效后通过get_tree().call_group("dialogue_system", "start_dialogue", nearby_npc.npc_name)通知对话系统;set_nearby_npc()由 NPC 调用以注册当前可交互对象;走路音效随移动状态自动播放/停止。
5.4 NPC 行为与交互
NPC 需要实现三个核心功能:随机巡逻、响应玩家交互、显示对话气泡。npc.gd提供了丰富的可导出配置项:
| 导出属性 | 默认值 | 说明 |
|---|---|---|
npc_name/npc_title | 张三 / Python工程师 | NPC 身份信息 |
sprite_frames | null | 自定义精灵帧资源 |
move_speed | 50.0 | 巡逻移动速度 |
wander_enabled | true | 是否启用巡逻 |
wander_range | 200.0 | 巡逻范围(围绕出生点) |
wander_interval_min/wander_interval_max | 3.0 / 8.0 秒 | 巡逻间隔区间 |
关键机制:
_ready()中add_to_group("npcs"),连接InteractionArea的body_entered/body_exited信号;_on_body_entered()检测到玩家(通过is_in_group("player"))后调用player.set_nearby_npc(self)完成"交互注册";_physics_process()中巡逻计时器递减,到期后在出生点wander_range范围内随机选点移动,到达后播放 idle;交互状态(is_interacting)下停止移动;update_dialogue()更新对话气泡文本并显示,10 秒后自动隐藏——主场景会定时调用它展示 NPC 之间的自主对话。
由于三个 NPC 共用同一场景,只需在 Main 场景的NPCs节点下分别设置npc_name/npc_title/sprite_frames即可获得三个性格各异但行为一致的角色。
6 前后端通信实现
6.1 API 客户端封装
Godot 前端通过api_client.gd封装所有 API 调用,并设为 AutoLoad 单例供全局使用。核心设计是使用 HTTPRequest 节点 + 信号回调而非 await,保证游戏流畅且允许多个脚本同时监听同一响应。客户端定义了四个信号:
signal chat_response_received(npc_name: String, message: String) signal chat_error(error_message: String) signal npc_status_received(dialogues: Dictionary) signal npc_list_received(npcs: Array)三个独立 HTTPRequest 节点(http_chat/http_status/http_npcs)对应三类请求,互不干扰。API 地址统一从全局配置单例 config.gd 读取:
const API_BASE_URL = "http://localhost:8000" const API_CHAT = API_BASE_URL + "/chat" const API_NPCS = API_BASE_URL + "/npcs" const API_NPC_STATUS = API_BASE_URL + "/npcs/status" const NPC_STATUS_UPDATE_INTERVAL = 30.0 # NPC状态更新间隔(秒)send_chat()用JSON.stringify构造请求体并通过http_chat.request()POST 到/chat;响应在_on_chat_request_completed()中校验 HTTP 状态码、解析 JSON 后发出chat_response_received信号。get_npc_status()在请求未完成时会跳过重复请求(检查STATUS_DISCONNECTED),避免轮询堆积。
6.2 对话 UI 实现
对话 UI 是CanvasLayer根节点,始终显示在最上层不被遮挡;Panel锚定屏幕底部作为背景。内部 6 个元素分工明确:NPCName显示名字、NPCTitle显示职位、DialogueText(RichTextLabel)显示对话内容(支持富文本)、PlayerInput(LineEdit)接收输入、SendButton/CloseButton发送与关闭。
dialogue_ui.gd的关键逻辑:
start_dialogue(npc_name)设置 NPC 信息、清空对话区、聚焦输入框并显示对话框;- 显示/隐藏对话框时通过
get_tree().get_first_node_in_group("player")通知玩家set_interacting(true/false),实现"对话中禁用移动"; send_message()用append_text以富文本追加玩家消息(青色),随后禁用输入框和发送按钮防止重复提交,异步等待响应;on_chat_response_received()收到匹配 NPC 的回复后以黄色追加显示,重新启用输入并聚焦;- 发送支持按钮点击与输入框回车(
text_submitted)两种触发方式。
6.3 主场景整合
main.gd负责协调所有组件并定时拉取 NPC 状态、更新对话气泡:_ready()中获取 APIClient 单例、连接npc_status_received信号并立即请求一次;_process()中每Config.NPC_STATUS_UPDATE_INTERVAL(默认 30 秒)轮询一次/npcs/status;收到更新后遍历所有 NPC 调用其update_dialogue(),让 NPC 之间的自主对话在气泡中呈现。即使玩家不与 NPC 交互,办公室也始终"有人在说话"。
至此前后端通信全部打通:玩家自由移动、与 NPC 自然语言对话、NPC 定时展示自主背景对话,整个系统以信号机制松耦合通信,易于维护扩展。
7 总结与展望
7.1 本章回顾
赛博小镇将 HelloAgents 框架与 Godot 引擎结合,创造出一个充满生命力的虚拟世界,核心成果可归纳为五点:
- 技术架构:采用"游戏引擎 + 后端服务"分离架构,Godot 负责画面与交互、FastAPI 负责 API 与状态管理、HelloAgents 负责 NPC 智能与记忆,各层可独立开发测试;
- NPC 智能体:用 SimpleAgent 为每个 NPC 创建独立智能体,通过精心设计的系统提示词塑造出严谨的 Python 工程师张三、善于沟通的产品经理李四、富有创意的 UI 设计师王五;
- 记忆与好感度:短期记忆保证对话连贯,长期记忆通过向量检索召回历史;五级好感度让 NPC 态度随互动从"陌生"演进到"挚友";
- 游戏场景:Godot 场景系统实现像素办公室、玩家控制、NPC 巡逻与对话 UI,场景实例化让新增 NPC 只需复制一份配置;
- 前后端通信:HTTP REST + 异步信号保证流畅性,API 客户端单例封装统一入口,对话 UI 提供自然交流体验。
7.2 扩展方向
赛博小镇只是起点,文档与仓库提供了清晰的扩展蓝图:
- 多人在线支持:引入 WebSocket 实时通信与数据库持久化,NPC 对每个玩家保持独立好感度;
- 任务系统:好感度达到阈值后 NPC 提供特殊任务(张三请玩家调试代码、李四请玩家收集反馈、王五请玩家评价设计),完成任务获奖励并提升好感度;
- NPC 之间互动:让张三与李四讨论产品需求、李四与王五讨论界面设计,后台自动进行,玩家可观察,世界更生动;
- 情感系统:在好感度之上增加开心、难过、生气等情绪状态,影响回复风格与行为;
- 动态事件系统:定期团队会议、生日派对、突发任务等事件增加变化性与趣味性;
- 更大的世界:扩展咖啡厅、图书馆、公园等场景,每个场景有不同的 NPC 与互动方式;
- 个性化学习:NPC 学习玩家偏好与习惯(如常聊 Python 则主动分享相关内容、晚间活跃则晚上更热情)。
7.3 思考与展望
AI NPC 为游戏带来了前所未有的可能性,但同时也面临现实挑战:成本——每次对话都调用 LLM API,大型多人在线场景成本可观;延迟——LLM 推理需要时间,网络不佳时玩家可能等待数秒;内容控制——LLM 生成内容不完全可控,需要精心设计提示词与内容过滤机制。
尽管如此,AI NPC 的未来依然充满希望:LLM 推理速度持续加快、成本不断下降,本地化小型模型快速发展,未来甚至可能在玩家设备上直接运行、完全无需网络请求。本项目的批量生成 + 即时响应混合模式、记忆与好感度一体化设计,也为其他需要大量 AI 调用的场景提供了可复用的工程思路。
【免费下载链接】hello-agents📚 《从零开始构建智能体》——从零开始的智能体原理与实践教程项目地址: https://gitcode.com/GitHub_Trending/he/hello-agents
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考