免费获取学习方案
ARTICLE DETAIL

资讯详情

深耕编程基础知识与建站技术分享的一线实战洞察。

OCR-Memory:为LLM智能体构建具备视觉回忆能力的长期记忆系统

OCR-Memory:为LLM智能体构建具备视觉回忆能力的长期记忆系统 1. 项目概述当AI智能体需要“长记性”最近在折腾LLM智能体LLM-powered Autonomous Agents的朋友估计都绕不开一个核心痛点记忆。一个智能体无论是帮你自动写代码、分析数据还是处理工作流如果它只能基于当前对话的上下文来行动那就像一个只有“七秒记忆”的鱼无法完成任何需要长期规划、持续追踪状态或从历史经验中学习的复杂任务。这就是“长视野”Long-Horizon任务对智能体提出的挑战。传统的解决方案比如简单地将所有历史对话记录塞进上下文窗口或者用向量数据库做语义检索都存在明显短板。前者受限于上下文长度后者则容易丢失对话中至关重要的视觉上下文和结构化操作序列。想象一下你让智能体帮你设计一个UI界面它画了几版草图你每次都在图上圈圈点点给出反馈。如果智能体只记得你说了什么“按钮再大点”但忘了你具体在图的哪个位置画的圈那后续的修改就无从谈起。这就是“OCR-Memory: Optical Context Retrieval for Long-Horizon Agent Memory”这个框架要解决的核心问题。它不是一个简单的记忆库而是一个为智能体打造的、具备“视觉回忆”能力的长期记忆系统。其核心创新在于“光学上下文检索”Optical Context Retrieval直白点说就是让智能体不仅能记住“说过什么”还能精准地回忆起“当时屏幕上显示的是什么”并将两者强关联起来。我最近在几个需要跨会话协作的自动化项目中深度试用了基于类似理念的架构感触颇深。它彻底改变了智能体处理多步骤、带界面交互任务的方式。接下来我就结合自己的实操经验拆解一下OCR-Memory的设计精髓、实现关键以及那些在文档里不会写的“坑”。2. 核心设计思路为什么是“光学”上下文要理解OCR-Memory得先看看现有记忆方案的局限性。目前主流的大模型智能体记忆方案大致分三类上下文窗口内记忆所有历史对话都保留在prompt里。优点是无损缺点是长度限制严格成本高且无关信息会干扰当前任务。向量数据库语义记忆将历史对话片段编码成向量存储需要时按语义相似度检索。这是目前最流行的方式但它有个致命伤它记忆和检索的是“语言描述”而非“视觉状态”。结构化摘要记忆由LLM定期对历史进行总结提炼关键点存储。这解决了信息压缩问题但摘要过程必然丢失大量细节尤其是视觉相关的空间、布局信息。OCR-Memory的突破点在于它认识到智能体尤其是与图形界面、文档、代码编辑器交互的智能体的许多操作是锚定在特定的视觉上下文中的。这个“光学上下文”可以是一张网页截图、一个IDE的代码编辑器界面、一个仪表盘的图表或者一个设计稿的当前状态。2.1 记忆单元的重新定义文本视觉的“时空胶囊”OCR-Memory将每一次智能体的“行动-观察”循环封装成一个包含多模态信息的记忆单元。这个单元远不止一段对话文本。一个完整的记忆单元通常包含文本上下文用户指令、智能体的思考过程、执行的操作如点击了哪个按钮、输入了什么命令、系统的文本反馈。光学上下文行动发生前或后的屏幕截图或特定UI区域的截图。这是核心。元数据时间戳、会话ID、任务ID、界面元素的坐标或定位信息如通过无障碍树Accessibility Tree获取的组件ID。这样当未来需要回忆“我之前是怎么调整这个图表格式的”时系统不仅可以检索相关的对话文本更能直接找到当时那个图表的截图以及在该截图上的具体操作位置。这为智能体提供了无与伦比的上下文还原能力。2.2 检索逻辑的双引擎驱动OCR-Memory的检索不是单一的向量搜索而是一个双引擎协同工作的过程语义检索引擎与传统方案类似基于文本上下文用户指令、智能体响应的嵌入向量进行相似度搜索找到可能相关的历史记忆单元。这一步是“广撒网”。光学特征检索引擎这是关键。系统会对当前智能体所处的视觉状态当前屏幕截图进行特征提取然后与记忆单元中存储的历史光学上下文进行匹配。匹配可以基于全局图像特征使用经过预训练的视觉模型如CLIP的视觉编码器提取图像嵌入向量进行相似度计算。适合找回整体布局相似的界面。局部关键点匹配对于需要精确定位的操作如点击了某个特定按钮可以结合截图中的SIFT、ORB等特征点或者更现代的基于深度学习的局部特征匹配技术。OCR文本匹配直接从当前截图和历史截图中提取文字进行关键词匹配。这对于找到包含特定标题、标签或数据的界面非常有效。最终系统会将两个引擎的检索结果进行融合重排优先返回那些既在语义上相关又在视觉上匹配的记忆单元。这确保了回忆的精准性和上下文的相关性。3. 核心模块拆解与实操要点要把OCR-Memory从理念落地需要构建几个核心模块。下面我结合一个具体的场景——让智能体学习并复现在一个复杂网页管理后台比如云服务控制台上的操作流程——来详细说明。3.1 光学上下文的捕获与编码捕获策略 不是所有时刻的屏幕都需要记录那会产生海量冗余数据。高效的捕获策略是关键。事件触发式捕获在智能体执行一个“动作”如click,type,navigate之前和之后各捕获一次屏幕或目标区域截图。这记录下了“操作现场”。关键状态变更捕获当检测到页面URL发生重大变化、主要页面内容区域刷新、或弹出重要模态框时自动捕获。用户指定捕获允许用户在指令中明确要求记录当前视图如“记住当前这个配置页面的样子”。编码与存储 存储原始截图成本极高。我们需要高效的编码。特征提取对于用于检索的图像立即使用视觉编码器例如从openai/clip-vit-base-patch32加载的视觉部分将其转换为特征向量例如512维向量。这个向量和文本嵌入向量一起存入向量数据库如Pinecone, Weaviate, 或开源的Chroma。原始图像存储特征向量用于检索但检索到之后我们需要展示原始的“光学上下文”给LLM。因此需要将压缩后的截图如转为WebP格式存储在一个对象存储如S3/MinIO或本地文件系统中并在数据库中存储其访问路径。绝对不要把Base64编码的图片直接塞进数据库字段。实操心得特征提取模型的选择很重要。CLIP模型在自然图像和UI截图上的表现都不错是很好的通用起点。如果场景非常垂直如全是代码编辑器截图可以考虑在特定数据集上微调效果提升显著。3.2 记忆的索引与混合检索实现这是系统的“大脑”。我们需要一个能同时处理文本向量和图像向量的索引。方案一双向量库联合查询这是最直观的方案。建立两个向量库一个存文本嵌入Text Embedding一个存图像嵌入Image Embedding。当查询时分别用查询文本和当前截图去两个库中检索各得到Top K个结果。对两组结果的记忆单元ID进行交集或加权合并。根据合并后的ID列表从主数据库中取出完整的记忆单元包含文本、图片路径等。# 伪代码示例双向量库检索融合 def hybrid_retrieve(query_text, current_screenshot, top_k5): # 1. 文本语义检索 text_embedding text_encoder.encode(query_text) text_results text_vector_db.similarity_search(text_embedding, top_k*2) # 多取一些 # 2. 图像特征检索 image_embedding image_encoder.encode(current_screenshot) image_results image_vector_db.similarity_search(image_embedding, top_k*2) # 3. 结果融合 (简单按ID计分) memory_scores {} for res in text_results: memory_scores[res.memory_id] memory_scores.get(res.memory_id, 0) res.score * 0.7 # 文本权重 for res in image_results: memory_scores[res.memory_id] memory_scores.get(res.memory_id, 0) res.score * 0.3 # 图像权重 # 4. 按总分排序取Top K sorted_memories sorted(memory_scores.items(), keylambda x: x[1], reverseTrue)[:top_k] final_memory_ids [mem_id for mem_id, _ in sorted_memories] # 5. 从主数据库如关系型数据库拉取完整记忆单元 full_memories db.fetch_memories_by_ids(final_memory_ids) return full_memories方案二多向量混合索引一些先进的向量数据库如Weaviate支持在一个集合Collection中为同一个对象存储多个向量。我们可以为每个记忆单元存储一个文本向量和一个图像向量。查询时可以执行多目标向量搜索即同时计算查询文本与文本向量、查询图片与图像向量的相似度并按自定义权重进行加权求和排序。这种方式在效率和结果一致性上通常更好。方案三元数据过滤 单向量检索如果视觉场景相对固定我们可以将视觉信息“文本化”作为元数据。例如对截图进行OCR提取出所有文字或者使用视觉模型生成图像的文本描述Caption。将这些文本描述作为元数据字段与原始操作文本拼接后再做嵌入。检索时只需用文本查询进行向量搜索即可这些元数据能极大地提升视觉相关回忆的准确率。这是一种轻量且有效的折中方案。注意事项权重调优是混合检索的“玄学”。文本权重和图像权重的比例如上例的0.7和0.3需要根据具体任务调整。对于强视觉依赖的任务如UI设计图像权重要提高对于逻辑推理为主的任务文本权重要更高。最好能准备一个小的测试集进行调优。3.3 记忆的触发、注入与遗忘机制智能体不可能在每次行动前都检索全部历史那样效率太低也会引入噪声。记忆触发 设计智能的触发条件决定“何时去回忆”。显式触发用户指令中包含“之前”、“上次”、“记得”等关键词或直接提问历史操作。隐式触发场景相似性当前屏幕的视觉特征与某个历史记忆高度相似时自动触发检索。任务连续性当前任务ID与历史任务ID相同时自动加载该任务相关的记忆链。困惑检测当LLM在思考中表现出对当前状态的不确定例如输出“我需要知道之前设置了什么参数”可以触发记忆检索来辅助。记忆注入 检索到的记忆如何提供给LLM不能一股脑全塞进上下文。相关性排序与过滤对检索到的记忆单元按相关性得分排序并设定一个阈值过滤掉低分记忆。格式化摘要将每个记忆单元的关键信息格式化成LLM易于理解的文本。例如[记忆ID: 123 | 时间: 2023-10-27 | 场景: 数据仪表盘配置] 用户指令“将销售额折线图改为柱状图”。智能体操作点击了图表右上角的“编辑”按钮坐标(850, 120)。操作后界面截图特征[描述性文字或图片路径]。上下文窗口管理将格式化后的记忆摘要作为系统提示词System Prompt的一部分或放在用户消息历史中提供给LLM。需要严格控制注入的记忆总量避免挤占当前任务所需的上下文空间。遗忘机制 记忆不是无限增长的。需要设计策略来淘汰低价值记忆。基于时间的衰减给每个记忆单元一个“活跃度”分数随着时间推移而衰减定期清理低分记忆。基于访问频率的淘汰长期未被检索和使用的记忆可以归档或删除。基于任务完成的清理当一个长期任务被标记为“完成”时可以清理掉该任务下大部分的过程性记忆只保留关键结果和总结。4. 实战构建一个简易OCR-Memory系统接入Java智能体结合网络热词中提到的“tencentdb agent memory接入java”这里给出一个更具体的、面向Java技术栈的集成思路。我们假设你已经有一个基于Spring Boot的Java智能体应用并使用腾讯云向量数据库Tencent Cloud Vector DB来存储向量。4.1 系统架构与组件选型智能体核心可以使用LangChain4J框架来构建智能体链它提供了与Java生态良好的集成。LLM通过API调用云端大模型如ChatGPT、文心一言、通义千问等。文本嵌入模型可以选择OpenAI的text-embedding-ada-002或开源的BGE-M3、jina-embeddings-v2等模型的API或本地部署。视觉编码模型使用OpenCLIP的Java封装可能需要通过JNI调用Python服务或者直接调用CLIP的API如果提供商支持。更轻量级的方案是使用MobileNet、EfficientNet等模型提取特征但通用性可能稍差。向量数据库腾讯云向量数据库Tencent Cloud Vector DB。它支持多向量检索和丰富的元数据过滤非常适合本场景。主数据库PostgreSQL或MySQL用于存储记忆单元的完整元数据、文本内容、图像存储路径等。图像存储腾讯云对象存储COS用于存放压缩后的截图。4.2 关键代码环节记忆的存储与检索1. 记忆封装实体// 伪代码使用Lombok简化 Data Entity Table(name agent_memory) public class AgentMemory { Id private String memoryId; private String sessionId; private String taskId; Lob private String textualContext; // JSON字符串包含用户指令、智能体响应等 private String opticalContextPath; // COS中截图文件的路径/URL private String opticalContextEmbeddingId; // 对应向量DB中图像向量的ID private String textualEmbeddingId; // 对应向量DB中文本向量的ID private LocalDateTime createdAt; private MapString, Object metadata; // 存储坐标、URL等其他元数据 }2. 记忆存储服务Service public class OCRMemoryService { Autowired private VectorDbService vectorDbService; // 封装腾讯云向量DB操作 Autowired private CosService cosService; // 封装腾讯云COS操作 Autowired private AgentMemoryRepository memoryRepository; Autowired private TextEmbeddingService textEmbeddingService; Autowired private ImageEmbeddingService imageEmbeddingService; public AgentMemory storeMemory(StoreMemoryRequest request) { // 1. 处理光学上下文 String imageUrl cosService.uploadScreenshot(request.getScreenshot()); float[] imageVector imageEmbeddingService.encode(request.getScreenshot()); String imageVecId vectorDbService.insertVector(imageVector, image_collection); // 2. 处理文本上下文 String fullText composeText(request.getUserInput(), request.getAgentAction(), request.getSystemResponse()); float[] textVector textEmbeddingService.encode(fullText); String textVecId vectorDbService.insertVector(textVector, text_collection); // 3. 保存主记忆记录 AgentMemory memory new AgentMemory(); memory.setMemoryId(UUID.randomUUID().toString()); memory.setOpticalContextPath(imageUrl); memory.setOpticalContextEmbeddingId(imageVecId); memory.setTextualEmbeddingId(textVecId); memory.setTextualContext(fullText); // ... 设置其他字段 return memoryRepository.save(memory); } private String composeText(String... parts) { // 将各部分文本组合成一段连贯的描述用于生成嵌入 return String.join(\n, parts); } }3. 混合检索服务public ListAgentMemory hybridRetrieve(String queryText, BufferedImage currentScreenshot, int topK) { // 1. 生成查询向量 float[] textQueryVector textEmbeddingService.encode(queryText); float[] imageQueryVector imageEmbeddingService.encode(currentScreenshot); // 2. 并行检索假设腾讯云向量DB支持多集合查询 ListVectorSearchResult textResults vectorDbService.search(textQueryVector, text_collection, topK * 2); ListVectorSearchResult imageResults vectorDbService.search(imageQueryVector, image_collection, topK * 2); // 3. 融合排序 (基于内存ID) MapString, Float memoryScoreMap new HashMap(); for (VectorSearchResult res : textResults) { // 从向量ID关联回主记忆ID这里需要你设计向量记录与主记忆的关联关系 String memId getMemoryIdByTextVecId(res.getVectorId()); memoryScoreMap.merge(memId, res.getScore() * 0.7f, Float::sum); } for (VectorSearchResult res : imageResults) { String memId getMemoryIdByImageVecId(res.getVectorId()); memoryScoreMap.merge(memId, res.getScore() * 0.3f, Float::sum); } // 4. 获取TopK的记忆ID ListString topMemoryIds memoryScoreMap.entrySet().stream() .sorted(Map.Entry.String, FloatcomparingByValue().reversed()) .limit(topK) .map(Map.Entry::getKey) .collect(Collectors.toList()); // 5. 从主数据库批量获取完整记忆 return memoryRepository.findAllById(topMemoryIds); }4.3 与智能体工作流的集成在LangChain4J的Agent执行循环中需要插入记忆钩子Hooks。// 伪代码展示思路 public class MemoryAwareAgent { private final Agent agent; private final OCRMemoryService memoryService; public String runWithMemory(String userInput, BufferedImage currentScreen) { // 1. 触发记忆检索 ListAgentMemory relevantMemories memoryService.hybridRetrieve(userInput, currentScreen, 3); // 2. 格式化记忆构建增强的Prompt String memoryContext formatMemoriesForPrompt(relevantMemories); String enhancedPrompt buildPrompt(userInput, memoryContext); // 3. 智能体执行 String agentResponse agent.execute(enhancedPrompt); // 4. 执行后捕获新的光学上下文例如操作后的屏幕状态 BufferedImage newScreen captureScreen(); // 5. 存储本轮记忆 StoreMemoryRequest memoryRequest new StoreMemoryRequest(); memoryRequest.setUserInput(userInput); memoryRequest.setAgentAction(agentResponse); // 这里可能需要解析出具体操作 memoryRequest.setScreenshot(newScreen); memoryService.storeMemory(memoryRequest); return agentResponse; } }5. 避坑指南与性能优化在实际部署中你会遇到一些预料之外的问题。5.1 常见问题与排查问题一检索结果不相关甚至干扰决策。排查首先检查文本嵌入模型是否适合你的领域。尝试用一些领域内关键词测试相似度。其次检查图像特征提取模型CLIP在非自然图像如纯代码终端上可能表现不佳。最后调整混合检索的权重可能是视觉权重太高把界面布局相似但语义无关的记忆找出来了。解决构建一个小的验证集人工评估检索结果系统性调整嵌入模型和权重。考虑加入元数据过滤比如只检索同一task_id下的记忆。问题二系统延迟明显影响智能体响应速度。排查瓶颈通常出现在1) 图像编码特别是使用大型视觉模型时2) 向量数据库检索尤其是当向量维度高、数据量大时3) 网络I/O从对象存储下载图片。解决图像编码异步化记忆存储时将截图上传和特征提取放入后台队列异步处理不阻塞主流程。检索时如果特征已计算好直接使用。向量索引优化确保腾讯云向量数据库的索引类型如HNSW参数设置合理。根据数据规模调整efConstruction和M参数。缓存对频繁访问的当前会话记忆或热门记忆进行缓存。降级方案在实时性要求极高的步骤可以先仅使用文本检索后续再异步进行视觉增强检索。问题三存储成本增长过快。排查截图是否未经压缩是否记录了太多无关紧要的中间状态解决图像压缩将PNG截图转换为有损但质量可接受的WebP格式通常能减少70%以上的体积。智能捕获优化触发策略只记录真正有价值的“决策点”和“状态变更点”而非每秒一帧。分层存储将近期高频访问的记忆放在高性能存储如SSD将老旧记忆归档到廉价存储如COS的归档层。实施遗忘策略如前所述定期清理低价值记忆。5.2 高级优化技巧记忆抽象与压缩不要让LLM直接阅读冗长的原始记忆文本。可以引入一个“记忆摘要”步骤定期用LLM将一系列细颗粒度的记忆单元总结成更高层次的“要点”或“经验法则”存储。例如将“点击A输入B点击C”的一系列操作总结为“配置X功能的流程是A-B-C”。检索时先检索高级摘要需要时再展开查看细节。基于图的记忆关联记忆单元之间不是孤立的。可以构建一个记忆图节点是记忆单元边代表它们之间的关联如时序先后、因果联系、针对同一UI元素。这样当检索到一个记忆时可以沿着图关系找到更多相关记忆形成更完整的上下文链条。视觉特征的增量更新对于长时间操作同一界面的任务如编辑文档相邻截图差异很小。可以只存储全量截图的关键帧对于中间帧只存储相对于前一帧的差异Diff和特征向量的增量变化大幅节省存储和计算。构建一个健壮的OCR-Memory系统绝非一日之功它需要你在多模态理解、向量检索、系统架构和大模型提示工程等多个方面进行细致的打磨。但一旦建成你所拥有的将是一个真正具备“情景记忆”能力的智能体它能像人类一样看着过去的“工作现场”照片继续未完成的工作这种能力的提升对于复杂自动化任务来说是颠覆性的。从我自己的实践来看在UI自动化测试、复杂软件教学助手、跨会话数据分析等场景下这种记忆系统的投入产出比非常高。
返回列表