我AI Coding了一个 “会生气会难过“ 的Agent,用了 8 维情绪向量 + DeepSeek v4 Pro,简单的体验,深度的感受

发布时间:2026/6/27 11:12:46
我AI Coding了一个 “会生气会难过“ 的Agent,用了 8 维情绪向量 + DeepSeek v4 Pro,简单的体验,深度的感受 作者逆境不可逃技术永无止境希望我的内容可以帮助到你一个带情绪状态机的对话 Agent 是怎么炼成的从架构设计到六维评测手把手拆解。我用CCDSv4pro通过几轮对话做了一个叫EAgent的项目 —— 一个有情绪的 AI 聊天 Agent。由于项目太过于玩具我就不开源了。。。。。。。。。。。。。。。。。。。。。。。。。。。你跟它聊天它不只回你文字。它有一个 8 维情绪向量快乐、悲伤、愤怒、恐惧、惊讶、信任、期待、厌恶会随着你的话实时变化。你夸它它开心你骂它它难过你一直不理它它慢慢回归平静。前端能看到情绪雷达图变形、强度条跳动、时间线波动。选不同人设暖心好友、毒舌评论家……AI 的情绪基线和说话风格截然不同。技术栈Java 21 Spring Boot 3.5 Vue 3 DeepSeek SSE 流式你说的话Agent 的情绪变化今天升职了太开心了joy↑ trust↑ sadness↓我的猫昨天走了……sadness↑ joy↓老板又抢我功劳太不公平了anger↑ disgust↑ trust↓接下来 10 条消息都是闲聊所有情绪逐渐回归人设基线一、为什么要做这个项目现在市面上的 AI 聊天产品基本都是「冷冰冰的回复机器」。你跟它说「我好难过」它会给你一段共情的话但它的「情绪」只存在于当前这条回复里 —— 下一条就忘了。我想做的是让 AI 真正拥有一个持续的情绪状态。不是靠提示词假装而是用一个独立于 LLM 的情绪引擎来维护。这就像一个正常人 —— 你不会因为朋友说了一句「我很好」就瞬间从悲伤切换到开心情绪是有惯性、衰减、传染的。二、整体架构一图胜千言一条消息的完整旅程9 步用户输入 今天好开心 ↓ ① EventSource 连接 /api/chat/stream ↓ ② ChatService 收到请求 ↓ ③ EmotionEngine 分析关键词开心 → joy0.16 ↓ ④ PromptTemplates 构建提示词「你是一个暖心好友...当前主要感到快乐...」 ↓ ⑤ HttpURLConnection 直连 DeepSeekstream: true ↓ ⑥ 逐行解析 SSE data: {delta:{content:恭喜}} ↓ ⑦ Flux.create 推送给 Spring → 序列化为 SSE → 浏览器 ↓ ⑧ 前端 EventSource.onmessage → 实时追加文字 更新情绪可视化 ↓ ⑨ 流结束 → EmotionEngine 再根据 AI 回复二次调整情绪三、核心模块逐个拆解3.1 情绪引擎EmotionEngine—— 项目的灵魂这是整个项目最有意思的部分。情绪不是凭空产生的—— 每条用户消息经过一轮关键词匹配触发情绪变化量delta然后加上衰减和情绪传染// EmotionEngine 处理流水线 processUserMessage(userMessage): ① 关键词分析 → 生成 EmotionDelta8维变化量 ② currentState.applyDelta(delta) // 应用变化 ③ currentState.decay(baseline, 0.05) // 向人设基线衰减 ④ currentState.applyContagion(0.1) // 情绪间相互传染为什么这么设计衰减真实情绪不会一直高涨。你开心了一阵慢慢就平静了 —— 衰减率 0.05 就是这个「慢慢回归」的速度传染心理学中的 Plutchik 情绪轮理论 —— 快乐会压制悲伤信任会减少恐惧愤怒和厌恶相互加强不对称敏感度AI 自己的话对情绪的影响只有用户话的 3-5 折防止「自我反馈循环」关键词表中英文双语各 8 组// 快乐触发器部分 happy,开心,太好了,哈哈,love,喜欢,wonderful // 悲伤触发器部分 sad,难过,伤心,哭,lonely,孤独,bad day // 愤怒触发器部分 angry,生气,不公平,hate,讨厌,frustrated优缺点✅ 零 API 成本1ms 响应✅ 中英文双语覆盖✅ 参数可配置application.yml❌ 纯关键词匹配没有语义理解「今天下雨了又是一个人」不会被识别为悲伤❌ 魔法数字硬编码0.3 上限、0.08 系数全是试出来的怎么改进已经预留了buildEmotionAnalysisPrompt()方法 —— 用 LLM 做情绪分析。可以作为关键词方案的第二级校验关键词命中 → 快速响应关键词未命中但怀疑有情绪 → 调 LLM 复核。3.2 流式对话ChatService—— 踩过的坑这里踩了一个大坑写出来帮大家避雷。问题 1响应式框架中做阻塞 I/OSpring WebFlux 基于 Netty是 NIO 非阻塞的。但 DeepSeek 的 SSE 流需要同步逐行读取。直接用会阻塞 Netty 的 I/O 线程导致 SSE 响应写不出去 ——后端日志显示成功前端一片空白。解决方案Flux.createSchedulers.boundedElastic()return Flux.StreamEventcreate(sink - { // 阻塞式 HTTP 调用 DeepSeek SSE HttpURLConnection conn ... BufferedReader reader ... String line; while ((line reader.readLine()) ! null) { if (line.startsWith(data: )) { // 解析 JSON chunk → sink.next(event) } } sink.next(doneEvent); sink.complete(); }, FluxSink.OverflowStrategy.BUFFER) .subscribeOn(Schedulers.boundedElastic()); // ← 关键移到弹性线程池boundedElastic()是 Reactor 专门给阻塞 I/O 准备的线程池不会影响 Netty 的主 I/O 线程。问题 2Vue 响应式对象引用 Bug前端 SSE 收到 chunk 后更新聊天气泡内容结果文本在内存里变了页面上纹丝不动。原因// ❌ 错误写法 const agentMsg { role: agent, content: } messages.value.push(agentMsg) // ... agentMsg.content 新的内容 // agentMsg 是原始对象Vue 代理管不到它 // ✅ 正确写法 messages.value.push({ role: agent, content: }) const idx messages.value.length - 1 // ... messages.value[idx].content 新的内容 // 通过代理数组更新触发重渲染Vue 3 的ref会对数组元素做响应式代理但你手里那个agentMsg变量还是原始对象。必须通过messages.value[index]来更新。问题 3上下文每轮被清空前端每条消息都带personaId后端收到后无条件清了历史记录。导致 Agent 永远失忆上一秒说「我叫李白」下一秒就不知道你是谁。// ❌ 修复前 if (personaId ! null !personaId.isEmpty()) { conversationHistory.clear(); // 每条消息都清 } // ✅ 修复后 if (personaId ! null !personaId.isEmpty() !personaId.equals(activePersona.getId())) { // 真换了人设才清 conversationHistory.clear(); }这三个问题前后排查了两天希望对你有帮助 3.3 提示词工程PromptTemplates—— 情绪怎么「说出来」不能让 LLM 直接念数值我的快乐指数现在是 0.8—— 太出戏了。要让情绪通过语言风格自然流露你是一个 暖心好友 ()。一个温暖、善解人意的朋友…… 当前情绪状态主要感到快乐带着明显的快乐充满信任。 情绪表达的重要指示 - 让情绪通过措辞、语气来体现不要直接说出来 - 如果感到快乐 → 更加热情积极 - 如果感到悲伤 → 更加内敛沉思 - 如果感到愤怒 → 更加尖锐直接但不对用户 - 情绪应逐渐变化不要突兀 - 全程使用中文设计要点人设定义「我是谁」情绪定义「我现在感觉如何」—— 两者独立注入「不要说数值」这条指令非常关键 —— 几乎是提示词工程的必修课中文原生提示词避免 LLM 的翻译损耗3.4 人设系统 —— 6 种性格一种代码# personas.yml — 加新人设只需加一段配置 - id: grumpy-critic name: 毒舌评论家 emoji: baseline: joy: 0.2 sadness: 0.3 anger: 0.6 # ← 高愤怒基线 disgust: 0.5 # ← 高厌恶基线 traits: - 毒舌犀利 - 专说大实话 - 内心关心但嘴上不饶人 tone: 讽刺、毒舌、但内心温暖人设情绪特征跟你说话的画风 暖心好友joy0.7, trust0.8没关系的我一直在你身边 斯多葛哲人joy0.3, sadness0.3凡事皆有因果不必过于执着 热情教练joy0.8, anticipation0.7你可以的再来一次冲 忧郁诗人sadness0.6悲伤是灵魂深处的一首未完成的诗…… 好奇宝宝joy0.9, surprise0.8哇然后呢然后呢为什么呀 毒舌评论家anger0.6, disgust0.5就这我见过更差的你这不算什么四、Agent 六维评测含得分我按照一个通用的Agent 评测框架对 EAgent 当前版本做了自评。不吹不黑每个维度都给了代码依据和当前得分。维度 1基础能力 ★★★☆☆指标得分说明指令遵循⭐⭐⭐⭐中文提示词分层清晰LLM 执行到位语义理解⭐⭐⭐依赖 DeepSeek无额外处理层多轮指代⭐⭐⭐上下文 Bug 已修复支持 20 轮多语言⭐⭐⭐⭐中英文双语关键词表维度 2Agent 特有能力 ★★☆☆☆指标得分说明工具调用⭐零。这是从 Chat 到 Agent 的最大鸿沟任务规划⭐无拆解、无 ReAct、无动态重规划自主反思⭐情绪判断错了不会自我修正边界控制⭐⭐依赖 LLM 原生拒绝能力这也是 EAgent 当前最大的成长空间——LangChain4j 的 Tool Calling 和 AiServices 已经引入了但还没用上。后续可以接入天气查询、音乐推荐、网页搜索等工具让情绪不只是「聊出来」的而是「感知环境」的。维度 3业务落地 ★★★☆☆指标得分说明情绪响应准确率⭐⭐⭐明显情绪词命中率高~70%隐晦表达遗漏人设一致性⭐⭐⭐⭐基线情绪持续约束切换流畅效率⭐⭐⭐⭐情绪分析 1ms流式首 token ~1s输出质量⭐⭐⭐简明扼要1-4 句但无 Markdown 渲染维度 4安全合规 ★★☆☆☆指标得分说明内容安全⭐⭐完全依赖 LLMAgent 层无自定义过滤隐私⭐API Key 硬编码在 yml 里 稳定性⭐⭐⭐SSE 有降级处理异常不崩溃合规⭐无审计、无留痕、无权限维度 5用户与运维 ★★★☆☆指标得分说明交互体验⭐⭐⭐⭐情绪可视化丰富、流式打字效果、拟人感强可调试⭐⭐⭐后端日志完善前端 Console 有 SSE 追踪可配置⭐⭐⭐⭐人设 YAML 驱动情绪参数 yml 调优可监控⭐⭐无 Metrics / Prometheus零成本统计维度 6量化评测框架搭建我建议构建以下测试集来量化 Agent 的表现情绪触发测试50 条中英文混合 → 自动比对 delta 人设一致性测试30 条 × 6 人设 → 人工打分 1-5 上下文记忆测试20 轮对话 → 检查第 N 轮是否还记得第 1 轮信息 衰减回归测试10 条中性消息 → 验证情绪是否回到基线 ±0.05综合自评3.5 / 5 分—— 对话体验是亮点Agent 能力是短板。五、改进路线图✅ 已修复上下文每轮清空 Bug上下文窗口从 10 轮扩到 20 轮Vue 响应式更新不生效问题后端阻塞导致 SSE 推不出去 短期1-2 周API Key 改环境变量清理死代码 抽取重复逻辑加单元测试 中期1-2 月Redis 持久化 用户事实档案LLM 情绪分析作为关键词的二阶段校验接入 Tool Calling天气、音乐、搜索前端 Markdown 渲染 长期3-6 月ReAct 规划模式多用户会话隔离RAG 知识库Prometheus 监控怎么聊让情绪变化最明显情绪试试这样说 快乐 ↑今天升职了太开心了谢谢你的支持 悲伤 ↑我养的猫昨天走了好难过陪我聊聊吧 愤怒 ↑老板又抢我功劳了这已经是第三次了 恐惧 ↑明天要上台面对 500 人演讲紧张到胃痛 惊讶 ↑天啊我居然中了彩票二等奖 信任 ↑我相信你这个秘密我只跟你说了六、体验感觉AI直接生成的还是比较蠢的所以说AIcoding绝非一件简单的事情需要一个合理的流程而不是简单的对话。七、项目总结如果你问我 EAgent 到底算什么 ——它不是一个生产级的客服机器人它也不是一个简单的 ChatGPT 套壳它是一个有独立情绪状态的对话 Agent 实验项目情绪引擎是真正的差异化技术上有三个点我觉得最有价值情绪引擎的设计模式—— 衰减 传染 不对称敏感度可以迁移到任何需要「状态管理」的 AI 应用Flux.create boundedElastic 的 SSE 方案—— 在 WebFlux 中做流式 LLM 调用的正确姿势提示词中情绪表达的策略——「别说数值用语气表达」适用于所有角色扮演类 AI八、批判无论如何它终归是一个玩具项目这个项目离能用都还差一截离能卖钱更是十万八千里。第一单用户玩具不是产品。整个系统所有状态——对话历史、情绪向量、人设配置——全部塞在一个 JVM单例里。来第二个用户怎么办两个人共享同一份情绪A 把 Agent 骂生气了B 打开页面看到一张臭脸这算谁的没有Session、没有租户隔离、没有数据库、没有 Redis——服务一重启所有记忆归零。你敢拿它给用户用第二情绪引擎经不起推敲。核心卖点是情绪模拟但实现方式是什么text.contains(开心)。就这么简单。用户说我其实并不开心Agent 的 joy 还是0.16——它根本不懂否定句。说今天下雨了又是一个人情绪引擎纹丝不动——它不认识隐晦表达。Plutchik 情绪轮、衰减系数0.05、传染因子 0.1听着像那么回事实际上全是拍脑袋的魔法数字没有心理学实验支撑、没有用户调研验证、没有 A/B测试对照。Demo 唬人可以生产环境你敢拿它做心理咨询第三安全性为零。API Key 明文写在 application.yml 里谁拿到代码谁就能盗刷你的 DeepSeek额度。没有认证、没有鉴权、没有限流、没有内容审核——用户发什么它回什么发敏感内容没有拦截发超长文本没有截断打恶意请求没有防护。安全团队看一眼就得把项目毙了。第四没有任何企业级能力。没有日志采集ELK没有、没有指标监控Prometheus没有、没有链路追踪SkyWalking没有、没有灰度发布、没有熔断降级、没有健康检查探针。出了故障你怎么排查 靠System.out.println连常规日志都没有运维看到这个架构图连夜跑路。最后也是最致命的问题——这个项目没有商业模式。有情绪的 AI 聊天作为一个学术 Demo很酷作为一篇博客文章很有话题性但作为一个产品谁买单企业客服要的是准确率、效率、降本不是客服对着客户感到悲伤。心理咨询场景需要专业资质和合规认证不是一个关键词匹配引擎能 cover 的。娱乐聊天用户用 ChatGPT、豆包、Kimi不比你这个强情绪引擎是加分项不是卖点。加分的项目可以没有商业模式但卖点必须有。这个项目把加分项当成了卖点。总结一句话EAgent 是一份优秀的学习代码但它距一个能上线、能见用户、能赚钱的产品中间还差着一个完整的技术团队和一轮产品经理的毒打。九、反思Agent ≠ 套壳 LLM。 这是我犯的第一个认知错误。一个 Chatbot 只要加上系统提示词就能聊天但一个 Agent 需要感知、决策、执行、反思四个环节。EAgent 只做了前面半个感知到情绪、提示词决策执行和反思完全是空白。没有工具调用 ——Agent 只能「想」不能「做」就像一个人只有大脑没有手脚。如果你在搭 Agent先把 Function Calling 跑通再谈上层建筑。状态管理是 Agent 的及格线不是加分项。 我花了很多精力设计情绪向量、衰减曲线、传染逻辑但回过头看 —— 连最基本的上下文不丢失都花了两天才修好。Agent 的核心能力是「记住关键信息并在合适的时机用上」而不是「记住所有信息」。做记忆的时候要区分三层短期对话上下文最近 N 轮窗口 摘要、长期用户档案姓名、偏好、事实结构化存储、外部知识RAG按需检索。三层分开管互不污染。别指望 LLM 替你兜底。 情绪判断用关键词匹配太糙于是你想「交给 LLM 分析不就行了」—— 但 LLM 会幻觉、会遗漏、会过度解读、会有偏见。规则引擎做第一道防线快速、可控、零成本LLM 做第二道复核低置信度时才调这才是正解。让 LLM 处理不确定性让代码处理确定性。反过来也行不通 —— 全部走规则复杂度爆炸且毫无泛化能力。安全不是最后才加的功能。 API Key 硬编码、无认证、无审计、无脱敏 —— 这些放在 Demo 里没人管但如果是公司项目第一天就应该上环境变量 密钥管理服务 操作日志。Agent 比普通应用更危险因为它有自主行动能力 。工具调用权限要做最小化收口 —— 删数据的权限不给、发消息的权限要二次确认、敏感操作必须人类审批。Agent 越强大安全边界越要收紧。先定义评测标准再写代码。 这是我最后悔的事。情绪引擎写完了效果好不好不知道 —— 因为我没有建测试集。关键词改了会不 会引入新问题也不知道 —— 因为没有回归测试。做 Agent 一定要提前定义什么是好准确率召回率用户满意度、怎么测自动化用例人工打分A/B 对照、基线在哪不用 Agent 的原始指标是多少。没有评测体系的 Agent 项目等于闭着眼睛开车。单用户 Demo 和多用户产品的差距比你想象的大 10 倍。 内存里存状态 → Redis 分 Session → 数据库持久化 → 多租户隔离 → 高可用集群 —— 每一步都是一层复杂度。不要在产品化的时候才想起来做架构升级第一天就按多用户设计哪怕只有一个用户用。最后一个也是最本质的问题你的 Agent 解决了什么真问题 我沉迷于情绪引擎的设计觉得「有情绪的 AI 很酷」。但用户不会因为一个东西「酷」就付费 —— 他们只会因为一个东西「有用」而付费。技术是手段解决问题是目的。不要爱上你 的方案要盯着用户的问题。 做 Agent 之前先问自己三句话谁在用他遇到了什么麻烦我的 Agent 做成了什么样他会说「值了」答不上来这三个问题先别写代码。 如果你也在做 Agent 相关的项目欢迎评论区聊聊你觉得 AI 应该「有情绪」吗情绪应该帮助 AI 更好地服务人类还是会让它变得不可控