🚀 情绪感知出行助手:LangChain4j 实战中的记忆、提示词与 Tool 调用最佳实践

项目结构

技术结构

Spring Boot 3.5.14 + LangChain4j 1.0.1-beta6 + MySQL + Redis

目前正在搭建,还在构思中,如果有好的issue,可以提出

仓库:Corner: 基于情绪感知的个性化微出行决策助手

总体上,项目的运行流程如下:

打开角落

输入手机号登录

进入首页

方式1: 点心情词云

方式2: 拉情绪滑块

方式3: 自由输入文字

POST /api/recommend mood=烦闷

LLM解析情绪

等待1-2秒

展示推荐结果

顶部: LLM共情语
(懂了,你需要安静地待着)

优先展示: 记忆库去过/收藏的

其次展示: 情绪地图匹配的

点击地点卡片

点不喜欢/换一批
(联网搜索)

点一键成行

查看地点详情

刷新推荐结果

展示出行卡片
(路线+时间+温馨提醒
5秒后自动返回主页)

回来后续可以在出行记忆中写感受

更新记忆库

项目搭建问题及项目机制

整个demo核心是POST /api/recommend的推荐机制

在项目构思时综合个人水平以及实现难度做了比较,主要思考如下:

  • 怎么更加快捷而不失精度的获取用户画像
  • 按照什么方式、根据什么用户信息给用户提供合适的个人服务
  • 如果用户信息给的不全,怎么去提供?是否有差异?

个人思考如下:

1.用户画像——通过大模型与用户的对话获取

思路

使用不同的AI工具中可以了解到如果要全面了解用户信息,有时候会提前要求填写一定的个人信息让大模型先行了解用户状况,然后才能给出比较合适的答案

但是如果每次都需要填写个人信息会有隐私风险,同时填写表格一定程度上影响用户使用体验。所以在这个项目中,设计应用在跟用户对话中通过LLM大模型进行分析补充用户画像

技术实现

看了一些材料,基于个人实现水平,目前设计是:

  1. demo阶段使用LLM大模型+系统提示词约束做基础的分析、提取,然后通过redis中间件和数据库实现持久化
  2. 之后使用Embedding 向量数据库和LLM将用户的输入截取出情绪相关的输入,然后进行向量化与向量数据库中的主要情绪表达做一个向量比较,提高精确度

2.推荐机制——情绪+收藏+位置三层架构

最初的规划是基于用户的情绪结合官方库进行推荐,但是会有用户推荐死板单一、推荐地点距离远近无法规范的问题。

在这个项目中,设计3层架构:

  1. 用户有收藏的地点/去过的地点+情绪匹配+用户位置
  2. 用户有收藏的地方+情绪匹配+官方库+用户位置
  3. 用户信息为空+情绪匹配+联网查找+用户位置

但是什么情况下调用哪一层让我想的比较久,单纯在前端设置使用不同查询方法的按钮是保底实现方法。但是更想做不需要用户自己去选择而是项目能够自行丝滑判断

技术实现

前端发送用户请求时需要携带用户的定位经纬度

  1. 使用Function Calling对不同场景进行判断哪一层的推荐机制最适合。在项目中我给AIService的系统提示词设置为:

    • 当用户有去过的或者收藏过的地点+情绪匹配+较近,优先调用
    • 如果上面的信息不超过3条,就调用第二层找有收藏的地方+情绪匹配+官方库
    • 如果前两层都不超过3条,那么进入第三层,通过联网搜索查找合适的地点(通过引入tavily依赖进行联网查询)

    同时对应设置AITool,确保实现效果

3.如何判断用户是在闲聊,还是在希望系统通过分析情绪进行推荐

一开始我打算两者都共同实现,即一个chat对话一个用于画像分析以及推荐。但是因为后端推荐控制类中只规划了一个对话,如果两者均实现就需要改动系统提示词以及添加新的Tool或者是新建一个方法,这样结构就偏冗余了

技术实现

进行边缘化分析。本身项目定位就是进行推荐,如果只是闲聊,那么其实有点偏离项目本身设定,最终把这个定为边界

4.其他编码问题

1:版本兼容性问题

错误:NoClassDefFoundError: ClientHttpRequestFactorySettings
原因:Spring Boot 4.0.6 与 LangChain4j 1.0.1 不兼容
解决:降级到 Spring Boot 3.5.14,并添加 spring-boot-starter-web

2:会话记忆 NPE

错误:NullPointerException 在 ChatMemoryService.getOrCreateChatMemory
原因:RedisChatMemoryRepository 使用 new 创建导致依赖未注入
解决:创建 ChatMemoryProvider Bean,通过构造器注入 RedisChatMemoryRepository

3:提示词模板为空

错误:@SystemMessage’s template cannot be empty
原因:@SystemMessage 注解未提供内容
解决:补充明确的系统提示词(角色+任务+输出格式+Tool 引导)

4:JSON 反序列化失败

错误:Cannot construct instance of Flux
原因:RecommendResponse 的 understanding 字段使用了 Flux
解决:改为 String 类型,LLM 返回的是普通 JSON 而非响应式流

5:推荐结果为空

现象:emotionMatches: []
原因:用户无历史记录时 Tool 直接返回空
解决:优化推荐逻辑,即使用户无记录也能从情绪标签匹配

最后

还有部分问题,像:

  1. 技术栈与依赖管理:在依赖中引入了阻塞式和流式输出,会造成一定的线程死锁或性能瓶颈。不要**“为了用而用”**
  2. 记忆与状态管理:ThreadLocal。一定程度上会导致AB用户信息泄露
  3. 推荐逻辑的“幻觉”与“控制力”:这个在测试中很明显,同时输出以后延迟太高(当前用了阻塞式就等了很久!!!),原文中也只是先一步测试
  4. 用户画像与隐私:当前我计划是通过对话提取用户画像,但是大模型提取准确率不高,且容易产生幻觉。可以后面通过跑WebSocket进行分析用户历史对话,而不是每次对话都分析不要把鸡蛋放在一个篮子里

后续继续修改

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐