[BUG] LanceDBStorage.list_records() returns oldest records instead of newest first due to premature query truncation

当使用 CrewAI 的 LanceDBStorage.list_records() 读取记忆记录时,返回结果可能不是最新的记录,而是最早的一批记录。优先排查该方法是否在排序前就按 limit + offset 截断了候选行:[BUG] LanceDBStorage.list_records() r

快速结论:当使用 CrewAI 的 LanceDBStorage.list_records() 读取记忆记录时,返回结果可能不是最新的记录,而是最早的一批记录。优先排查该方法是否在排序前就按 limit + offset 截断了候选行:[BUG] LanceDBStorage.list_records() returns oldest records instead of newest first due to premature query truncation。

适用环境:Issue 已确认的环境为 macOS Sonoma、Python 3.12、crewAI 1.15.21、crewAI Tools 1.15.21、Venv 虚拟环境;存储后端为 LanceDB。

最快修复方案:暂无已合并并正式发布的一步修复方案。Issue 讨论中提出的修复已由 PR #7418 提交,但该 PR 的合并状态与对应正式版本号在 Issue 中没有明确证据,可优先尝试升级到包含该 PR 的 crewAI 版本,或参考 PR 改动调整 list_records()。

注意事项:Issue 中的建议修复方案是不带 limit 参数调用 _scan_rows(scope_prefix),依赖默认 _SCAN_ROWS_LIMIT(50,000)限制扫描量;当同一 scope 内记录数接近或超过该上限时,仍可能影响结果完整性。此外,PR 最终实现采用了批量流式 top-k 堆,与 Issue 正文中的简单排序补丁并不完全一致,修改源码时应以实际 PR 内容为准。

问题场景

用户在 CrewAI 中使用 LanceDB 作为记忆存储后端,先向 LanceDBStorage 写入多条 MemoryRecord,再调用 storage.list_records(limit=3) 期望拿到最新 3 条记录。按文档,list_records() 应返回“newest first”,按 created_at 降序排列,但实际返回的是最早写入的几条记录。

报错原文

Returned IDs: ['rec_2', 'rec_1', 'rec_0']

Expected: ['rec_9', 'rec_8', 'rec_7']
Actual:   ['rec_2', 'rec_1', 'rec_0']

[BUG] LanceDBStorage.list_records() returns oldest records instead of newest first due to premature query truncation

原因分析

问题出在 lib/crewai/src/crewai/memory/storage/lancedb_storage.py 的 list_records() 实现。当前逻辑先执行 self._scan_rows(scope_prefix, limit=limit + offset),即先只取前 limit + offset 行,然后才按 created_at 降序排序并切片。这样扫描阶段拿到的是表中较早的行,最新记录在排序之前就被截断,永远不会进入最终结果。

Issue 正文给出的复现结果中,期望返回 ['rec_9', 'rec_8', 'rec_7'],实际返回 ['rec_2', 'rec_1', 'rec_0'],正是这种“先截断、后排序”顺序导致的表现。

环境排查

  • 确认 crewAI 版本是否为 1.15.21 或存在相同 list_records() 截断逻辑的版本。
  • 确认 Python 版本为 3.12(Issue 环境),并检查是否在 Venv 虚拟环境中运行。
  • 确认安装的 crewAI Tools 版本与 crewAI 版本一致(Issue 中均为 1.15.21)。
  • 确认使用的是 LanceDBStorage,而非 QdrantEdgeStorage 等其他存储实现。
  • 检查本地 lancedb_storage.py 中 list_records() 是否存在 _scan_rows(scope_prefix, limit=limit + offset) 这类排序前截断。
  • 确认测试或业务代码中的 created_at 确实按预期递增,以排除数据层时间戳本身乱序。

解决步骤

  1. 先运行 Issue 中的最小复现脚本:向临时目录中的 LanceDBStorage 写入 10 条 created_at 依次递增的记录,再调用 list_records(limit=3),确认返回是否为最早 3 条而非最新 3 条。
  2. 检查当前 crewAI 版本是否已包含 PR #7418 的修复;如已包含,直接升级或安装对应版本后重新验证。
  3. 如仍需本地修改,参考 Issue 建议:在 list_records() 中不要传入 limit=limit + offset,而是在 scope 内扫描候选行(使用默认 _SCAN_ROWS_LIMIT),按 created_at 降序排序后再执行 records[offset : offset + limit]。
  4. 如需与 PR #7418 最终实现保持一致,应参考该 PR:将 scope 过滤和仅含元数据(排除向量 embedding)的投影下推到 LanceDB,再用以 created_at 为键、容量为 offset + limit 的有界 top-k 最小堆流式筛选,最后返回 limit/offset 切片。
  5. 修改后补充回归测试,至少覆盖:默认查询返回最新记录、带 offset 的分页顺序、以及 scope 过滤下的排序正确性。

验证方法

重新执行复现脚本,确认 list_records(limit=3) 返回 ['rec_9', 'rec_8', 'rec_7'],并且返回顺序为 created_at 降序。再补充带 offset 的分页用例,确认分页切片发生在正确排序之后,而不是在扫描阶段提前截断。

参考来源

crewAIInc/crewAI #7394

关联修复 PR:crewAIInc/crewAI #7418

GamsGo AI

AI 工具推荐

想把多个 AI 模型放在一个入口?

GamsGo AI 集成 ChatGPT、DeepSeek、Gemini、Claude、Midjourney、Veo 等常用模型,适合写作、绘图、视频和日常 AI 工作流。

了解 GamsGo AI

推广链接:通过此链接购买,我可能获得佣金,不影响你的价格。

这个方案解决了吗?

celebrityanime
celebrityanime
文章: 29794

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注