fix(memory): 修复 Hindsight provider 生命周期的三处缺陷
HindsightProvider 被 build_memory_manager 每轮重新构造,以下三个问题
都源于这个无状态特性未被正确处理:
1. document_id 每轮漂移 —— 原 _reset_retain_batch 用微秒时间戳拼接,
导致同一 thread 的每轮 retain 落到不同文档,文档数随对话轮数线性膨胀。
改为按 thread_id 确定性派生 (thread-{thread_id}),同一 thread 累积成一份。
2. retain_every_n_turns 失效 —— 进程内 _turn_counter 每轮被 initialize
重置,retain_every_n_turns > 1 时永不触发。新增 turn_index 参数贯穿
sync_turn/sync_all,由 MemoryMiddleware 按对话历史 (count_user_turns)
算出并传入;_turn_counter 仅作缺省退路。
3. aiohttp ClientSession 泄漏 —— 缓存的客户端从不 shutdown,且 _run_coro
每次新建事件循环使缓存客户端失效,触发 "Unclosed client session"。
改为客户端即开即关:_run_with_client 在 finally 保证关闭,_client_factory
作为可覆盖的测试注入点。
测试:test_hindsight_provider.py 全量重写 (含 TestDocumentId /
TestRetainEveryNTurnsAcrossRecreation / TestClientLifecycle),
新增 test_memory_message_processing.py,test_memory_manager.py 补
turn_index 透传用例。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@
38 lines
1.3 KiB
Python
38 lines
1.3 KiB
Python
"""Memory V2 - message_processing 辅助函数测试。"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from langchain_core.messages import AIMessage, HumanMessage
|
|
|
|
from deerflow.agents.memory.message_processing import count_user_turns, filter_messages_for_memory
|
|
|
|
|
|
class TestCountUserTurns:
|
|
def test_empty(self):
|
|
assert count_user_turns([]) == 0
|
|
|
|
def test_counts_only_human_messages(self):
|
|
messages = [
|
|
HumanMessage(content="问题一"),
|
|
AIMessage(content="回答一"),
|
|
HumanMessage(content="问题二"),
|
|
AIMessage(content="回答二"),
|
|
]
|
|
assert count_user_turns(messages) == 2
|
|
|
|
def test_ignores_ai_messages(self):
|
|
messages = [AIMessage(content="a"), AIMessage(content="b")]
|
|
assert count_user_turns(messages) == 0
|
|
|
|
def test_works_on_filtered_output(self):
|
|
# count_user_turns 设计上吃 filter_messages_for_memory 的输出
|
|
raw = [
|
|
HumanMessage(content="第一轮"),
|
|
AIMessage(content="", tool_calls=[{"name": "x", "args": {}, "id": "1"}]),
|
|
AIMessage(content="第一轮回答"),
|
|
HumanMessage(content="第二轮"),
|
|
AIMessage(content="第二轮回答"),
|
|
]
|
|
filtered = filter_messages_for_memory(raw)
|
|
assert count_user_turns(filtered) == 2
|