
hello-agents 赛博小镇 NPC 好感度系统实现指南基于 LLM 情感分析的动态关系与对话风格引擎【免费下载链接】hello-agents 《从零开始构建智能体》——从零开始的智能体原理与实践教程项目地址: https://gitcode.com/datawhalechina/hello-agents在《从零开始构建智能体》(hello-agents) 的赛博小镇项目中NPC 不再是只会机械应答的对话机器人它们拥有 0-100 的好感度数值、五档关系等级会根据玩家与它们的每一次对话自动调整态度并实时改变回复的语气与详细程度。本篇指南以 AFFINITY_SYSTEM_GUIDE.md 为核心主体结合仓库中的RelationshipManager源码、NPCAgentManager集成逻辑与 FastAPI 接口实现完整讲解好感度系统的架构设计、情感分析提示词、动态更新规则、REST API 与调试方法。读完本文你将掌握如何在多智能体系统中用 LLM 实现对话 → 情感分析 → 数值更新 → 风格调整的完整闭环并能直接复刻到自己的 Agent 游戏或角色扮演应用中。一、系统概述让 NPC 拥有人情味赛博小镇AI Town是一个基于 HelloAgents 框架的 AI NPC 对话系统包含张三Python 工程师、李四产品经理、王五UI 设计师三位 AI 居民。好感度系统的核心目标是根据玩家与 NPC 的对话内容自动调整好感度并让好感度反过来影响后续对话的风格和态度。从 README.md 可知该项目将好感度系统列为五大核心功能之一智能对话、记忆系统、好感度系统、NPC 自主行为、日志系统并作为教材第 15 章的配套案例。好感度系统的本质是用一个 LLM Agent 做情感裁判把一段自然语言对话转化为结构化的数值决策再由数值驱动另一套对话生成逻辑。四大核心能力自动情感分析使用 LLM Agent 分析对话情感从四个维度评估——玩家态度友好/中立/不友好、对话内容积极/中立/消极、互动质量深入/一般/敷衍、情感倾向赞美/批评/中性。好感度动态调整友好对话提升好感度1 到 10批评对话降低好感度-3 到 -15更新后自动收敛在 0-100 范围内。关系等级系统将连续的好感度数值映射为五档离散关系等级。对话风格调整好感度等级和修饰词被注入 NPC 的上下文提示词实时改变回复语气。二、架构设计RelationshipManager 的核心结构好感度系统的核心类位于 relationship_manager.py其类结构如下RelationshipManager ├── affinity_scores: Dict[str, Dict[str, float]] # NPC好感度存储 {npc: {player: score}} ├── analyzer_agent: SimpleAgent # 情感分析Agent ├── get_affinity(npc_name, player_id) # 获取好感度 ├── set_affinity(npc_name, affinity, player_id) # 设置好感度 ├── analyze_and_update_affinity(...) # 分析并更新好感度 ├── get_affinity_level(affinity) # 获取关系等级 ├── get_affinity_modifier(affinity) # 获取对话风格修饰词 └── get_all_affinities(player_id) # 获取所有NPC好感度数据存储结构好感度使用嵌套字典存储relationship_manager.py# 格式: {npc_name: {player_id: affinity_score}} self.affinity_scores: Dict[str, Dict[str, float]] {}第一层以 NPC 名称为键第二层以玩家 ID 为键——这意味着同一 NPC 可以对不同玩家持有不同好感度天然支持多人游戏场景。未初始化时的默认好感度为50.0见get_affinity方法。分析 Agent 的创建在__init__中RelationshipManager基于 HelloAgents 框架的SimpleAgent创建了一个名为AffinityAnalyzer的专职分析 Agentrelationship_manager.pyself.analyzer_agent SimpleAgent( nameAffinityAnalyzer, llmllm, system_promptself._create_analyzer_prompt() )这种用 Agent 做子任务的设计是 HelloAgents 多智能体思想的典型体现对话生成由各 NPC 自己的 Agent 负责情感分析则交给独立的专业 Agent两者通过 LLM 接口解耦互不干扰。三、情感分析提示词设计LLM 如何读懂情绪情感分析效果的好坏几乎完全取决于系统提示词的设计。RelationshipManager中_create_analyzer_prompt()方法构造了一套完整的分析框架relationship_manager.py其关键设计可拆解为四层1. 角色定位你是一个情感分析专家,负责分析对话中的情感倾向,判断是否应该改变NPC对玩家的好感度。2. 分析维度结构化【分析维度】 1. 玩家态度: 友好/中立/不友好 2. 对话内容: 积极/中立/消极 3. 互动质量: 深入/一般/敷衍 4. 情感倾向: 赞美/批评/中性3. 变化规则量化约束【好感度变化规则】 - 赞美、感谢、请教: 3 到 8 - 友好问候、正常交流: 1 到 3 - 普通闲聊、中性话题: 0 - 批评、质疑、不耐烦: -3 到 -8 - 侮辱、攻击、恶意: -8 到 -154. 输出格式强制 JSON【输出格式】(严格遵守JSON格式,不要添加任何其他文字) { should_change: true/false, change_amount: -15到10之间的整数, reason: 简短说明原因(10字以内), sentiment: positive/neutral/negative }提示词中还内置了 5 个 few-shot 示例友好问候、批评工作、普通闲聊、赞美工作、请教学习各一例并反复强调三条硬性约束只输出 JSON、change_amount 必须是整数、reason 必须在 10 字以内。这种角色 维度 规则 示例 约束的提示词结构是保证 LLM 输出稳定、可解析的关键值得在任意 LLM 结构化输出场景中复用。三级容错解析不信任裸 JSONLLM 并不总能保证输出合法 JSON因此_parse_analysis方法实现了三级递进式解析策略relationship_manager.py直接解析先用json.loads(response)尝试整体解析截取解析若失败用response.find({)和response.rfind(})截取首尾花括号之间的内容再解析正则兜底若仍失败用正则分别匹配should_change、change_amount、reason、sentiment四个字段should_change_match re.search(rshould_change\s*:\s*(true|false), response, re.IGNORECASE) change_amount_match re.search(rchange_amount\s*:\s*(-?\d), response)全部失败时才返回默认值{should_change: False, change_amount: 0, ...}并打印警告日志。这一设计保证了系统即使在 LLM 输出不规范时也不会崩溃是生产级 JSON 解析的示范代码。四、好感度更新核心流程analyze_and_update_affinity是系统的核心方法relationship_manager.py完整流程如下1. 玩家发送消息 ↓ 2. NPC生成回复 ↓ 3. 情感分析Agent分析对话 ├── 分析玩家态度 ├── 评估对话内容 ├── 判断情感倾向 └── 计算好感度变化量 ↓ 4. 更新好感度 ├── 当前好感度 变化量 ├── 限制在0-100范围 └── 检查等级变化 ↓ 5. 保存到记忆系统 └── 记录好感度和情感信息更新逻辑要点方法先构造分析提示拼接玩家消息与 NPC 回复调用分析 Agent然后根据 JSON 解析结果处理if analysis[should_change]: current_affinity self.get_affinity(npc_name, player_id) new_affinity current_affinity analysis[change_amount] new_affinity max(0.0, min(100.0, new_affinity)) # 限制在0-100 self.set_affinity(npc_name, new_affinity, player_id)值得注意的两处工程细节范围钳制max(0.0, min(100.0, affinity))出现在set_affinity中确保任何路径写入的好感度都不会越界relationship_manager.py等级前后对比更新前后分别调用get_affinity_level据此判断是否发生关系等级跨越供上层日志记录 关系等级提升事件异常兜底整个流程被 try/except 包裹分析失败时返回changed: False且保持原好感度不会中断主对话。与 NPC Agent 的集成chat 方法六步流水线好感度并不是孤立运行的它被深度集成进NPCAgentManager.chat方法agents.py的完整对话流水线① 记录对话开始日志系统 ② 读取当前好感度 → 构造【当前关系】上下文等级修饰词 ③ 检索 NPC 记忆 → 构造【之前的对话记忆】上下文 ④ 拼接增强提示词 → 调用 NPC Agent 生成回复 ⑤ 分析并更新好感度 → 记录变化详情到日志 ⑥ 保存对话到记忆携带好感度与情感元数据其中第 ② 步是关键好感度被格式化为一段注入 NPC 提示词的上下文agents.pyaffinity_context f【当前关系】 你与玩家的关系: {affinity_level} (好感度: {affinity:.0f}/100) 【对话风格】{affinity_modifier} 这段上下文与检索到的记忆一起拼接成enhanced_message后交给 NPC 的SimpleAgent。第 ⑥ 步则将好感度、变化量、情感倾向写入记忆元数据metadata中的affinity、affinity_change、sentiment字段实现好感度 → 记忆 → 后续对话的长期闭环。这也解释了 AFFINITY_SYSTEM_GUIDE.md 中与记忆系统协同工作的教学要点。五、关系等级系统与对话风格修饰词连续的好感度数值被映射为五档离散等级映射逻辑集中在get_affinity_levelrelationship_manager.py好感度区间关系等级对话风格修饰词get_affinity_modifier0-20陌生冷淡疏离不太愿意多说回答简短20-40熟悉礼貌但略显生疏回答简洁40-60友好礼貌友善正常交流保持专业60-80亲密友好热情愿意多聊会主动关心对方80-100挚友非常热情友好像老朋友一样亲切愿意分享私人话题等级与修饰词是两套独立的映射函数等级用于显示与 UI修饰词用于驱动对话风格。从源码可以看到两个函数均按 80 / 60 / 40 / 20的阈值级联判断区间下界由get_affinity_level中的比较符决定例如好感度恰为 80 时属于挚友而非亲密。对话风格变化的直观对比同一句问候在不同的好感度下NPC 的回复风格差异明显见 AFFINITY_SYSTEM_GUIDE.md 示例 3好感度/等级玩家你好最近怎么样NPC 李四的反应30熟悉还行吧。简短回答70亲密挺好的最近在做一个很有意思的项目你要不要听听热情详细90挚友哈哈老朋友最近忙得不行但很充实。对了上次你问的那个问题我找到答案了亲切主动六、好感度变化规则与两个完整示例变化规则速查表对话类型变化量示例赞美、感谢、请教3 到 8你真棒 谢谢你 能教教我吗友好问候、正常交流1 到 3你好 最近怎么样普通闲聊、中性话题0今天天气不错批评、质疑、不耐烦-3 到 -8这个不太好 真的吗侮辱、攻击、恶意-8 到 -15你太烂了注意该表格与提示词中的规则严格一致但需指出变化量最终由 LLM 自行判断表格是提示词中的指导规则而非硬编码逻辑——这正是系统的灵活性所在也意味着实际变化可能存在合理浮动。示例 1好感度提升含等级跨越初始好感度: 50 (友好) 第一次对话: 玩家: 你好,很高兴认识你! 张三: 你好!我也很高兴认识你。 好感度: 50 - 55 (友好问候) 第二次对话: 玩家: 你的代码写得真棒! 张三: 谢谢!我最近在研究新技术,你对这个感兴趣吗? 好感度: 55 - 63 (赞美工作) → 关系等级提升: 友好 - 亲密 第三次对话: 玩家: 能教教我吗? 张三: 当然可以!我很乐意分享。你想从哪里开始? 好感度: 63 - 69 (请教学习)示例 2好感度降低含等级下降当前好感度: 69 (亲密) 批评对话: 玩家: 你这个代码写得太烂了! 张三: 抱歉,我会改进的... 好感度: 69 - 61 (批评工作) → 关系等级降低: 亲密 - 友好这些示例与提示词中的 few-shot 示例一一对应如果你在本地复现时发现数值与示例不完全一致属于 LLM 判断的正常波动可通过调整提示词规则收紧。七、REST API 接口如何把好感度暴露给前端好感度系统通过 FastAPI 暴露为三个核心接口全部实现在 main.py 中。启动后端后python main.py即可通过http://localhost:8000/docs访问 Swagger 文档在线调试。1. 获取单个 NPC 好感度GET /npcs/张三/affinity?player_idplayer响应{ npc_name: 张三, player_id: player, affinity: 65.0, level: 亲密, modifier: 友好热情,愿意多聊,会主动关心对方 }实现上该接口先校验 NPC 是否存在不存在返回 404再委托npc_mgr.get_npc_affinity内部依次调用get_affinity/get_affinity_level/get_affinity_modifier。2. 获取所有 NPC 好感度GET /affinities?player_idplayer响应{ player_id: player, affinities: { 张三: { affinity: 65.0, level: 亲密, modifier: 友好热情,愿意多聊,会主动关心对方 }, 李四: { affinity: 50.0, level: 友好, modifier: 礼貌友善,正常交流,保持专业 }, 王五: { affinity: 72.0, level: 亲密, modifier: 友好热情,愿意多聊,会主动关心对方 } } }该接口直接透传RelationshipManager.get_all_affinities的遍历结果便于游戏 UI 一次性展示全镇 NPC 的态度。3. 设置 NPC 好感度测试/初始化用PUT /npcs/张三/affinity?affinity80player_idplayer响应{ message: 已设置张三对玩家的好感度, npc_name: 张三, player_id: player, affinity: 80.0, level: 挚友, modifier: 非常热情友好,像老朋友一样亲切,愿意分享私人话题 }该接口在 main.py 中额外做了参数校验好感度必须在 0-100 之间否则返回 400 错误。适合测试时快速把某位 NPC 的关系调到指定等级。对话接口POST /chat Content-Type: application/json {npc_name: 张三, message: 你好,你在做什么?}对话接口本身不返回好感度但每次调用都会触发一次情感分析与好感度更新随后通过GET /npcs/张三/affinity即可看到变化。八、测试方法验证系统的完整手段方法 1测试脚本AFFINITY_SYSTEM_GUIDE.md 描述了一个test_affinity.py测试脚本的用法cd backend python test_affinity.py测试覆盖内容基本好感度功能、好感度提升/降低、关系等级变化、对话风格调整、好感度渐进提升。需要说明的是当前仓库的backend目录中尚未包含该脚本文件指南中给出了其核心测试思路——构造三类消息friendly_messages [你好!, 你真棒!, 能教教我吗?]、critical_messages [这个不好, 你太烂了]、neutral_messages [今天天气不错, 嗯]并调用analyze_and_update_affinity断言结果你可以参考指南自行编写或直接改用下面的 API 方式验证。方法 2API 测试推荐启动后端服务配置步骤详见 SETUP_GUIDE.mdcd backend python main.py访问 API 文档http://localhost:8000/docs依次测试对话POST /chat查看好感度GET /npcs/张三/affinity查看所有好感度GET /affinities手动调整可选PUT /npcs/张三/affinity?affinity80运行环境说明从 config.py 可以看到好感度系统与整个赛博小镇后端共用一套配置默认使用 ModelScope 推理服务LLM_BASE_URL默认https://api-inference.modelscope.cn/v1/模型默认Qwen/Qwen2.5-72B-Instruct通过.env文件中的LLM_API_KEY注入密钥。若未配置密钥Settings.validate()会给出警告NPCAgentManager将退化为模拟模式——此时好感度管理器不会被初始化见 agents.py 的if self.llm:判断因此完整体验好感度系统必须配置可用的 LLM 密钥。九、调试技巧观察好感度的每一步变化1. 查看好感度变化日志系统内置了完整的对话日志体系logger.py每次对话会按时间戳记录到backend/logs/dialogue_YYYY-MM-DD.log。与好感度相关的关键日志片段 当前好感度: 50.0/100 (友好) 正在分析好感度变化... 好感度变化: 50.0 - 55.0 (5.0) 原因: 友好问候 情感: positive 关系等级变化: 友好 - 亲密日志中会自动为正向变化添加 、负向变化添加 符号log_affinity_change中依据change_amount正负选择等级跨越时额外记录 事件。使用python view_logs.py tail可实时查看最新日志。2. 检查情感分析结果如需查看 LLM 的原始分析输出可在 relationship_manager.py 中添加调试输出print(f情感分析结果: {analysis})3. 观察日志级别判断问题结合好感度为什么没有变化的排查思路优先检查日志中analyze_and_update_affinity返回的reason字段——若频繁出现解析失败说明 LLM 输出 JSON 不稳定应优先检查提示词约束与 API 密钥对应的模型能力。十、常见问题FAQQ1好感度为什么没有变化可能原因对话内容过于中性如嗯、今天天气不错分析 Agent 判定should_change: falseLLM 响应解析失败_parse_analysis返回默认值可在日志中看到JSON解析失败警告处于模拟模式未配置 LLM_API_KEY好感度管理器未初始化。解决方法使用更明确的情感表达检查日志中的情感分析结果调整情感分析提示词。Q2好感度变化太快/太慢解决方法修改 relationship_manager.py 中提示词的变化量范围调整情感分析提示词中的规则描述使用PUT /npcs/{npc_name}/affinity接口对应set_npc_affinity手动设置初始值。Q3对话风格没有明显变化可能原因好感度差异不够大40 与 60 之间的修饰词差异确实不如 20 与 80 明显NPC 的 system_prompt 没有充分利用好感度修饰词——从 agents.py 可以看到修饰词是通过【对话风格】段落注入的如果自定义了 NPC 提示词模板需要保留该注入点。解决方法增大好感度差异例如对比 20 vs 80在 system_prompt 中强调对话风格的重要性。十一、调优建议与扩展方向敏感度调节好感度变化的敏感度完全由提示词控制可按需调整修改 relationship_manager.py 中_create_analyzer_prompt更敏感增大变化量范围例如 -20 到 15更保守减小变化量范围例如 -5 到 5更细腻添加更多分析维度如幽默感、亲密度、话题相关性更简单简化分析规则减少 few-shot 示例。建议的扩展方向从指南的下一步与教学价值章节出发可扩展的方向包括在 Godot 前端显示好感度 UI配合helloagents-ai-town项目中的对话 UI 脚本、基于好感度解锁特殊对话与任务、加入随时间衰减的好感度机制、以及用 Embedding 相似度替代纯 LLM 判断使成本更低。十二、教学价值总结好感度系统虽然只是赛博小镇的一个模块但它完整演示了 LLM 在 Agent 系统中的四个高复用设计模式LLM 情感分析实战如何设计结构化分析提示词、如何用 few-shot 稳定输出、如何对 JSON 响应做三级容错解析数值状态机设计如何把连续数值0-100映射为离散等级并用等级驱动另一套生成逻辑对话风格修饰词多系统协同集成好感度如何与记忆系统保存元数据、日志系统记录变化轨迹、API 层暴露查询与设置联动用户体验设计如何让 NPC 更有人情味——从冷冰冰的问答变成会记仇、会亲近、会寒暄的虚拟居民。这套LLM 分析器 数值状态 上下文注入的架构与具体实现核心代码集中在 relationship_manager.py、agents.py 与 main.py 三个文件中可以直接迁移到任意需要关系模拟的 Agent 场景游戏 NPC、虚拟伴侣、客服情绪管理、教育陪练等。结合 SETUP_GUIDE.md 完成环境配置后用几条赞美与批评消息你就能亲眼看到一位 NPC 从陌生走向挚友的全过程。【免费下载链接】hello-agents 《从零开始构建智能体》——从零开始的智能体原理与实践教程项目地址: https://gitcode.com/datawhalechina/hello-agents创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考