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

作者:逆境不可逃
技术永无止境
希望我的内容可以帮助到你!!!!
一个带情绪状态机的对话 Agent 是怎么炼成的?从架构设计到六维评测,手把手拆解。
我用CC+DSv4pro通过几轮对话做了一个叫 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 分析关键词:"开心" → joy+0.16
↓
④ PromptTemplates 构建提示词:「你是一个暖心好友...当前主要感到快乐...」
↓
⑤ HttpURLConnection 直连 DeepSeek,stream: true
↓
⑥ 逐行解析 SSE data: {"delta":{"content":"恭喜"}}
↓
⑦ Flux.create 推送给 Spring → 序列化为 SSE → 浏览器
↓
⑧ 前端 EventSource.onmessage → 实时追加文字 + 更新情绪可视化
↓
⑨ 流结束 → EmotionEngine 再根据 AI 回复二次调整情绪
三、核心模块逐个拆解
3.1 情绪引擎(EmotionEngine)—— 项目的灵魂
这是整个项目最有意思的部分。
情绪不是凭空产生的—— 每条用户消息经过一轮关键词匹配,触发情绪变化量(delta),然后加上衰减和情绪传染:
// EmotionEngine 处理流水线
processUserMessage(userMessage):
① 关键词分析 → 生成 EmotionDelta(8维变化量)
② 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/O
Spring WebFlux 基于 Netty,是 NIO 非阻塞的。但 DeepSeek 的 SSE 流需要同步逐行读取。直接用会阻塞 Netty 的 I/O 线程,导致 SSE 响应写不出去 ——后端日志显示成功,前端一片空白。
解决方案:Flux.create + Schedulers.boundedElastic()
return Flux.<StreamEvent>create(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 线程。
问题 2:Vue 响应式对象引用 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: "讽刺、毒舌、但内心温暖"
| 人设 | 情绪特征 | 跟你说话的画风 |
|---|---|---|
| 🤗 暖心好友 | joy=0.7, trust=0.8 | "没关系的,我一直在你身边" |
| 🧘 斯多葛哲人 | joy=0.3, sadness=0.3 | "凡事皆有因果,不必过于执着" |
| 💪 热情教练 | joy=0.8, anticipation=0.7 | "你可以的!再来一次!冲!" |
| 🌙 忧郁诗人 | sadness=0.6 | "悲伤是灵魂深处的一首未完成的诗……" |
| 🌟 好奇宝宝 | joy=0.9, surprise=0.8 | "哇!然后呢然后呢?为什么呀?" |
| 😼 毒舌评论家 | anger=0.6, disgust=0.5 | "就这?我见过更差的,你这不算什么" |
四、Agent 六维评测(含得分)
我按照一个通用的 Agent 评测框架,对 EAgent 当前版本做了自评。不吹不黑,每个维度都给了代码依据和当前得分。
维度 1:基础能力 ★★★☆☆
| 指标 | 得分 | 说明 |
|---|---|---|
| 指令遵循 | ⭐⭐⭐⭐ | 中文提示词分层清晰,LLM 执行到位 |
| 语义理解 | ⭐⭐⭐ | 依赖 DeepSeek,无额外处理层 |
| 多轮指代 | ⭐⭐⭐ | 上下文 Bug 已修复,支持 20 轮 |
| 多语言 | ⭐⭐⭐⭐ | 中英文双语关键词表 |
维度 2:Agent 特有能力 ★★☆☆☆
| 指标 | 得分 | 说明 |
|---|---|---|
| 工具调用 | ⭐ | 零。这是从 Chat 到 Agent 的最大鸿沟 |
| 任务规划 | ⭐ | 无拆解、无 ReAct、无动态重规划 |
| 自主反思 | ⭐ | 情绪判断错了不会自我修正 |
| 边界控制 | ⭐⭐ | 依赖 LLM 原生拒绝能力 |
📌 这也是 EAgent 当前最大的成长空间——LangChain4j 的 Tool Calling 和 AiServices 已经引入了但还没用上。后续可以接入天气查询、音乐推荐、网页搜索等工具,让情绪不只是「聊出来」的,而是「感知环境」的。
维度 3:业务落地 ★★★☆☆
| 指标 | 得分 | 说明 |
|---|---|---|
| 情绪响应准确率 | ⭐⭐⭐ | 明显情绪词命中率高(~70%),隐晦表达遗漏 |
| 人设一致性 | ⭐⭐⭐⭐ | 基线情绪持续约束,切换流畅 |
| 效率 | ⭐⭐⭐⭐ | 情绪分析 <1ms,流式首 token ~1s |
| 输出质量 | ⭐⭐⭐ | 简明扼要(1-4 句),但无 Markdown 渲染 |
维度 4:安全合规 ★★☆☆☆
| 指标 | 得分 | 说明 |
|---|---|---|
| 内容安全 | ⭐⭐ | 完全依赖 LLM,Agent 层无自定义过滤 |
| 隐私 | ⭐ | 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 更好地服务人类,还是会让它变得不可控?

更多推荐


所有评论(0)