
这个项目名一看就很有意思——“Driving directions from LLM memory”从LLM的记忆里拿驾驶路线。听起来很玄乎实际跑通之后你会发现这其实是Agent类应用里非常典型的一种落地形态让大模型记住用户的长期偏好再结合外部地图能力把模糊意图转化成精确指令。我花了一周多时间把这个思路从零搭成了一个可用的Demo中间踩了不少坑也总结出了几个关键设计点。这篇东西就把整个项目的思路、技术拆解、实操代码和排查经验一次性讲清楚给同样对LLM记忆系统感兴趣的朋友一个可以直接抄作业的参考。1. 项目思路为什么要把路线指引交给LLM记忆1.1 传统路线规划的痛点现在打开任何一款主流地图应用路线规划的逻辑本质上都是一套规则加优化算法输入起点终点系统根据路况、拥堵、距离、时间预算算出一条或多条候选路线再让你自己挑。这套流程在大多数时候够用但你仔细想想它其实一点都不“懂你”。比如我每天通勤从家到公司有两条路一条走主干道距离短但早晚高峰很容易堵另一条绕一点但沿路信号灯少反而平均耗时更稳定。导航每次都让我选甚至默认推荐距离最短的那条。每次我都要手动点几下才能切到“我的路线”。再比如说我周末常去某个商场导航里存的地址是商场正门可我习惯的路线是从地下车库某个入口进去这种细粒度的偏好传统地图应用几乎没有表达方式。这背后的根本问题在于传统导航的交互模型是“一次性查询”它不保存你的历史选择不学习你的偏好更不会在你说“今天走老路”的时候自动理解。所有信息都需要你明确告诉它哪怕你已经用了它三五年。1.2 LLM记忆带来的交互范式转变“Driving directions from LLM memory”这个项目的出发点就是想把交互范式换掉。让LLM作为路线的“大脑代理”它拥有一个记忆系统能记住你过去的行为、选择、习惯然后你用一句很自然的会话式指令——比如“去公司别走高架”——它就能从记忆里提取出真正符合你需求的路线方案再调用地图服务去执行。这里有两个核心转变第一个转变是从“用户迁就机器”变成“机器适配用户”。以前是我必须按照地图应用提供的输入格式去给出起点终点现在只需要说一句话剩下的大量隐性信息——具体的起点坐标、常用路线、偏好路段——由LLM结合记忆自动补全。第二个转变是从“单次查询”变成“持续记忆”。每次导航行为结束后系统会把路线选择、时间、交通状况、你的最终决定比如你有没有中途切换路线写回记忆存储。日积月累这个系统越来越像一个熟悉你的私人司机他知道你着急的时候会选高速知道你在雨天的备选方案知道你新搬了家之后最常去的位置。选择记忆方案还有一个很重要的原因是成本效率。你不可能把一个人几个月的历史路线数据全部塞进上下文窗口里那样既不经济也不可靠。通过内存检索的方式系统只在一句新指令到达时从记忆库中取回最相关的几十条记录这个量级对LLM来说是完全没有压力的。2. 核心技术拆解LLM记忆的构建与应用2.1 短期记忆与长期记忆的工程实现在设计记忆系统时我参考了认知科学里的“工作记忆”和“长期记忆”分类并把它映射到工程结构上。短期记忆在这个项目里对应的是当前对话会话的内部状态。比如用户刚刚说过“我今天要去机场”在接下来几轮里“机场”这个目的地就需要保持在活动上下文中。实现上我用了最常规的方式让LLM在处理输入时携带一段标准的会话历史同时用一个JSON结构专门存放“当前行程状态”包括当前起点、终点、携带乘客等关键字段。长期记忆则完全不同。它需要跨越会话保存核心是结构化和向量化两类记录结构化记录包含用户常去地点、常用路线、偏好时间段、路线选择历史。这些适合用键值对或表格存储查询条件很明确。向量化记录包含自然语言形式的偏好描述比如“上次下雨天为了不晒选择了一条林荫路”这类数据无法通过简单的SQL筛选需要转换为向量用相似度检索。我在项目里使用的是SQLite加向量检索配套的方案。SQLite存储结构化数据轻量可靠向量部分则借助本地嵌入模型生成特征向量再用一个支持余弦相似度的索引来检索。没有依赖重型数据库因为Demo阶段更关注逻辑闭环。2.2 从记忆到路线上下文检索与偏好建模这一步是整个系统的核心环节。当用户说了一句新指令系统要决定调用哪些记忆片段来辅助决策。我当时把流程设计成三个子步骤。第一步是意图识别用LLM判断用户是在查询历史记录、规划新路线还是修改已有行程。第二步是记忆召回根据意图中的地点、时间、天气、动作等信息从记忆存储中检索相关片段比如用户过去在这个时间段去过某个地点或者曾经表达过对某类路段的偏好。第三步是路线生成把召回的片段和当前请求一起封装进提示词让LLM输出结构化的路线请求参数。偏好建模这里花了很多功夫。用户不会直接说“记录一下我喜欢走江边路”更多时候是从行为里推断出来的。我实现了一个“行为推断模块”每一次行程结束后将系统推荐路线和用户实际选择进行对比如果用户偏离了推荐路线就提取这条实际路线的特征路段性质、是否为避开拥堵的替代路、通行时间写进记忆库。比如有两次用户在傍晚选择了一条较长的滨河路而这条路线比推荐路线平均多花八分钟系统就会生成一条隐含偏好“傍晚时段用户倾向于选择滨河路线可能与景观较好或拥堵躲避有关。”这样偏好就不是用户显式告诉LLM的而是系统从行为里“挖”出来的。当然这种推断不一定永远正确所以项目里还保留了用户确认的交互环节当系统检索到强相关的偏好记录但置信度不高时会先向用户确认“是否仍按之前傍晚经常走的滨河路线”3. 实操实现从零搭建记忆驱动的路线指引Demo3.1 环境准备与数据组织先说明一下我搭建Demo时用的技术栈整体偏轻量方便快速验证思路LLM推理本地部署的量化开源LLM模型通过OpenAI兼容接口调用嵌入模型一个轻量的中文文本嵌入模型用于把偏好描述转成向量向量存储使用sqlite-vec扩展在SQLite表里直接建向量索引地图服务调用某地图服务的路线规划API用于真实路网数据数据组织上我在SQLite里建了三张核心表-- 地点表存储用户常去地点的语义信息 CREATE TABLE places ( id INTEGER PRIMARY KEY, name TEXT, address TEXT, lat REAL, lng REAL, tag TEXT, created_at TIMESTAMP ); -- 行程记录表存储每次导航会话的路线与结果 CREATE TABLE trips ( id INTEGER PRIMARY KEY, start_place_id INTEGER, end_place_id INTEGER, route_choice TEXT, traffic_condition TEXT, departure_time TIMESTAMP, actual_duration INTEGER, preferred INTEGER, note TEXT, FOREIGN KEY(start_place_id) REFERENCES places(id), FOREIGN KEY(end_place_id) REFERENCES places(id) ); -- 偏好记忆表同时存原文和向量 CREATE TABLE preferences ( id INTEGER PRIMARY KEY, content TEXT, embedding BLOB, source_trip_id INTEGER, confidence REAL, created_at TIMESTAMP );建好这三张表之后整个系统的基础就有了。后面所有记忆的写入、检索都在这些表上展开。注意trips表里的preferred字段非常关键它标记了这次行程是否是用户最终确认采纳的方案只有被标记为preferred1的记录才会被用来生成新的偏好记忆。3.2 核心代码实现与参数说明下面的代码是记忆写入侧的简化版本。每次导航结束后系统会对比推荐路线和实际路线如果用户实际走的路线和推荐路线不一致就会触发偏好生成逻辑。import json import sqlite3 from datetime import datetime def record_trip(user_id, start_place, end_place, rec_route, actual_route, traffic): 记录一次行程并判断是否生成本地偏好。 rec_route: 系统推荐路线的描述 dict actual_route: 用户实际走的路线的描述 dict traffic: 当时的交通状况字符串 conn sqlite3.connect(memory.sqlite) cur conn.cursor() # 写入行程记录 cur.execute( INSERT INTO trips (start_place_id, end_place_id, route_choice, traffic_condition, departure_time, actual_duration) VALUES (?, ?, ?, ?, ?, ?) , ( start_place[id], end_place[id], json.dumps(actual_route, ensure_asciiFalse), traffic, datetime.now().isoformat(), actual_route[duration_minutes] )) trip_id cur.lastrowid # 比较推荐路线和实际路线判断是否需要生成偏好 is_preferred 0 if rec_route[route_id] ! actual_route[route_id]: # 用户实际选择偏离了推荐说明有潜在偏好 preference_text build_preference_text( start_place, end_place, rec_route, actual_route, traffic ) # 生成向量并写入偏好表 embedding generate_embedding(preference_text) cur.execute( INSERT INTO preferences (content, embedding, source_trip_id, confidence, created_at) VALUES (?, ?, ?, ?, ?) , ( preference_text, embedding.tobytes(), trip_id, 0.7, datetime.now().isoformat() )) is_preferred 1 cur.execute(UPDATE trips SET preferred ? WHERE id ?, (is_preferred, trip_id)) conn.commit() conn.close() return trip_id def build_preference_text(start_place, end_place, rec_route, actual_route, traffic): 构造偏好描述的文本供LLM和向量检索使用 return ( f用户从{start_place[name]}到{end_place[name]} f在{traffic}条件下没有选择系统推荐的{rec_route[summary]} f而是选择了{actual_route[summary]} f实际耗时{actual_route[duration_minutes]}分钟。 )上面代码的注释我想多说几句。这里比较推荐路线和实际路线用的是route_id这是一个字符串标识每条路线由地图服务生成时就有唯一的ID。不要拿路线名称去比较不同服务商返回的名称格式很不一致。还有confidence0.7是我给这个偏好记录预设的置信度不属于用户显式确认因此置信度不会很高后续在召回时需要结合用户反馈调整权重。下一个关键部分是记忆召回与提示词组装。当用户说“去公司”时系统要检索出相关的历史语义def recall_relevant_memories(request_text, top_k10): 根据当前请求文本从偏好记忆库中召回最相关的记录。 query_embedding generate_embedding(request_text) conn sqlite3.connect(memory.sqlite) # 使用sqlite-vec的向量搜索语法 rows conn.execute( SELECT id, content, distance FROM preferences WHERE embedding MATCH ? ORDER BY distance LIMIT ? , (query_embedding.tobytes(), top_k)).fetchall() # 过滤掉距离超出阈值的记忆片段 relevant [ {id: r[0], content: r[1]} for r in rows if r[2] 0.35 ] conn.close() return relevant def compose_prompt(user_input, place_info, recalled_memories): memory_block \n.join( f- 回忆 {m[id]}: {m[content]} for m in recalled_memories ) prompt f 你是用户的路线规划助手。下面是从用户历史记忆中检索到的相关偏好 {memory_block} 用户当前说{user_input} 已知用户地点信息 {json.dumps(place_info, ensure_asciiFalse)} 请根据用户历史偏好结合当前请求输出一个结构化的路线规划请求包含 - start_lat, start_lng - end_lat, end_lng - waypoints途径点列表根据历史偏好可以自行添加 - route_preference可选值fastest, stable, scenic, avoid_highway - human_readable_reason说明为什么这样规划 只输出JSON对象。 return prompt这里distance 0.35的阈值是我经过二十多组测试后确定的经验值不同嵌入模型的最佳阈值差别很大建议你在自己的环境里做一次阈值扫描。方法是准备一批已知相关的记忆对和一批完全不相关的干扰项计算它们各距离值的分布取两条分布曲线交叉点的距离作为阈值。3.3 关键优化与调试技巧把记忆系统接入LLM之后遇到的最大问题就是“提示词越长模型越容易迷糊”。我最初把所有召回的记忆都拼进提示词结果模型经常混淆不同时间段的路线选择甚至把昨天去商场的记忆安到今天上班的行程上。后来我加了一个“记忆筛选小模型”让嵌入模型先做一次粗筛再用LLM对粗筛后的记忆做一次相关性判别只保留真正与当前请求语义一致的片段。另一个优化是关于意图识别的稳定性。最初我直接用LLM做自由文本意图解析输出常常不固定后来改成few-shot方式在提示词里给出三个示例并明确要求只输出预设的几个枚举值。输出准确率一下子从七成提到了九成以上。如果要处理更多意图类型可以做一个简单的分类器微调但Demo阶段用few-shot完全够用。还有一个细节是坐标系处理。地图服务返回的路线会经过多个路段每个路段有大量坐标点。把这些原始坐标全部存进记忆是浪费空间。我在实现时只保存了“特征点”——路线的起点、终点、中途转向点和路线所属的“廊道”。所谓廊道就是一条宽几十米的带状范围这样即使用户有不同的起终点也能判断两条路线是否大致沿着同一走廊。4. 常见问题与排查技巧实录4.1 记忆过期与路线失效用户地址会变道路也会变记忆如果不做时效管理就可能给出过时甚至危险的指引。项目里会遇到这样的情况用户三个月前经常去某老地址今天突然说“去老地方”系统从记忆中检索到旧地址推荐了一条已经封闭的道路。这个问题靠一条策略解决时间衰减。给每条记忆记录一个时间戳和一个衰减系数在做向量召回时把记忆的原始相似度乘上一个时间衰减因子。我实现的经验是import math from datetime import datetime, timezone def time_decayed_score(score, created_at, half_life_days30): age_days (datetime.now(timezone.utc) - created_at).days decay 0.5 ** (age_days / half_life_days) return score * decay这里half_life_days30意味着每一篇记忆在三十天后相关性减半。如果某篇记忆被用户后续行为再次确认则会刷新时间戳相当于“强化记忆权重”。这个机制和人类记忆的遗忘曲线有异曲同工之处实现起来也不复杂但它直接让系统的召回结果从“偶尔莫名其妙”变得可靠得多。4.2 上下文污染与多轮对话管理初学者很容易忽略一个细节长对话里前面几轮提到的中间地点会在用户说完新需求后仍然留在上下文里被LLM误当成新路线的途经点。比如用户先说“先送孩子去学校”然后说“再到公司”系统很可能把“学校”设定为从家到公司路线的强制途经点哪怕早上并不顺路。我解决这个问题是在提示词里设置一个“行程状态重置”机制。每当用户启用一个新的导航请求系统就清空上一轮对话中的地点状态仅保留明确的“继续”“加上”这类延续性指令才考虑合并。代码里就是一个reset_context标记在生成新路线前判断是否重置。还有一种更隐蔽的污染偏好记忆之间互相矛盾。用户某两周一直走高速后来换成国道旧记忆并没有被删除下一次指令把两种记忆都集齐了模型就变得犹豫不决。我在处理时加入一个“最新偏好优先”的规则在提示词里特别注明“若存在与最近五天内的偏好冲突的旧记忆以最近为准”。4.3 实测表现与效果评估整个Demo在十几个测试场景里跑下来的结果让我对这套方案有了更实感的判断。我把测试分成三类一是通勤场景二是弹性出行场景三是临时新地点场景。通勤场景效果最好。因为我事先注入了足够多的历史行程数据系统在用户说“去公司”时能准确从记忆中调出常用路线并给出合理的替代方案。比如用户平时走主干道但今天下雨系统从偏好表里检索到“雨天倾向于走避免拥堵的滨河路”于是推荐了一条替代路线。弹性出行场景表现也不错比如“找个这周末能带狗去的地方”系统检索相关偏好后能结合地图搜索结果返回候选地址。但语义匹配的精度还有提升空间偶尔会召回完全无关的记忆。临时新地点场景是最弱的因为记忆库里没有任何相关信息系统退化为普通的路线规划器优势不明显。用数据说话的话我标注了三十条测试用例通勤场景的正确率在86%左右弹性场景只有70%临时场景基本和传统导航一致。这个结果表明记忆驱动的路线规划适合高频、强习惯性出行场景低频或首次去的地方传统方式反而更省事。5. 给同样在做LLM记忆应用的人一些建议整个项目跑下来我最大的感受是“LLM记忆”这个技术方向的价值不在于模型本身有多强而在于你怎么设计写和读两侧的逻辑。写的时候不能只依赖用户显式表达要用系统性的行为追踪去挖掘隐含偏好读的时候要用向量检索加上规则筛选而不是把所有记忆一股脑丢进上下文。如果你打算复现类似的项目我建议从单场景做起先把“通勤偏好”这一个维度做透。用一到两星期的历史导航数据灌进记忆库然后跑用户日常对话观察系统做出的路线建议是否符合实际习惯。这一关过了再考虑多场景、多用户的并发情况。最后分享一个踩坑后的心得千万不要跳过用户确认这一层。我最初把偏好推断做成全自动认为系统推荐即正确结果有几次用户在界面里手动切回了默认路线系统记录下来的偏好反而变成了噪音。后来我在生成新偏好时加入一条隐式确认规则——用户连续两次走同一偏离路线才写入高置信度的偏好记忆——整个系统的表现才稳定下来。有时候给机器一点“怀疑的空间”反而比盲目自信更实用。
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。