fix title generation with dynamic context reminder (#2830)

This commit is contained in:
DanielWalnut 2026-05-09 18:22:58 +08:00 committed by GitHub
parent 0d1053ca44
commit f76e4e35c8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 49 additions and 3 deletions

View File

@ -53,6 +53,11 @@ def _extract_date(content: str) -> str | None:
return m.group(1) if m else None
def is_dynamic_context_reminder(message: object) -> bool:
"""Return whether *message* is a hidden dynamic-context reminder."""
return isinstance(message, HumanMessage) and bool(message.additional_kwargs.get(_DYNAMIC_CONTEXT_REMINDER_KEY))
def _last_injected_date(messages: list) -> str | None:
"""Scan messages in reverse and return the most recently injected date.
@ -61,7 +66,7 @@ def _last_injected_date(messages: list) -> str | None:
are not mistakenly treated as injected reminders.
"""
for msg in reversed(messages):
if isinstance(msg, HumanMessage) and msg.additional_kwargs.get(_DYNAMIC_CONTEXT_REMINDER_KEY):
if is_dynamic_context_reminder(msg):
content_str = msg.content if isinstance(msg.content, str) else str(msg.content)
return _extract_date(content_str)
return None

View File

@ -9,6 +9,7 @@ from langchain.agents.middleware import AgentMiddleware
from langgraph.config import get_config
from langgraph.runtime import Runtime
from deerflow.agents.middlewares.dynamic_context_middleware import is_dynamic_context_reminder
from deerflow.config.title_config import get_title_config
from deerflow.models import create_chat_model
@ -61,6 +62,10 @@ class TitleMiddleware(AgentMiddleware[TitleMiddlewareState]):
return ""
@staticmethod
def _is_user_message_for_title(message: object) -> bool:
return getattr(message, "type", None) == "human" and not is_dynamic_context_reminder(message)
def _should_generate_title(self, state: TitleMiddlewareState) -> bool:
"""Check if we should generate a title for this thread."""
config = self._get_title_config()
@ -77,7 +82,7 @@ class TitleMiddleware(AgentMiddleware[TitleMiddlewareState]):
return False
# Count user and assistant messages
user_messages = [m for m in messages if m.type == "human"]
user_messages = [m for m in messages if self._is_user_message_for_title(m)]
assistant_messages = [m for m in messages if m.type == "ai"]
# Generate title after first complete exchange
@ -91,7 +96,7 @@ class TitleMiddleware(AgentMiddleware[TitleMiddlewareState]):
config = self._get_title_config()
messages = state.get("messages", [])
user_msg_content = next((m.content for m in messages if m.type == "human"), "")
user_msg_content = next((m.content for m in messages if self._is_user_message_for_title(m)), "")
assistant_msg_content = next((m.content for m in messages if m.type == "ai"), "")
user_msg = self._normalize_content(user_msg_content)

View File

@ -7,6 +7,7 @@ from unittest.mock import AsyncMock, MagicMock
from langchain_core.messages import AIMessage, HumanMessage
from deerflow.agents.middlewares import title_middleware as title_middleware_module
from deerflow.agents.middlewares.dynamic_context_middleware import _DYNAMIC_CONTEXT_REMINDER_KEY
from deerflow.agents.middlewares.title_middleware import TitleMiddleware
from deerflow.config.title_config import TitleConfig, get_title_config, set_title_config
@ -44,6 +45,22 @@ class TestTitleMiddlewareCoreLogic:
assert middleware._should_generate_title(state) is True
def test_should_generate_title_with_dynamic_context_reminder(self):
_set_test_title_config(enabled=True)
middleware = TitleMiddleware()
state = {
"messages": [
HumanMessage(
content="<system-reminder>\n<memory>User prefers Python.</memory>\n</system-reminder>",
additional_kwargs={_DYNAMIC_CONTEXT_REMINDER_KEY: True},
),
HumanMessage(content="帮我总结这段代码"),
AIMessage(content="好的,我先看结构"),
]
}
assert middleware._should_generate_title(state) is True
def test_should_not_generate_title_when_disabled_or_already_set(self):
middleware = TitleMiddleware()
@ -243,6 +260,25 @@ class TestTitleMiddlewareCoreLogic:
prompt, _ = middleware._build_title_prompt(state)
assert "<think>" not in prompt
def test_build_title_prompt_uses_real_user_message_with_dynamic_context_reminder(self):
_set_test_title_config(enabled=True)
middleware = TitleMiddleware()
state = {
"messages": [
HumanMessage(
content="<system-reminder>\n<memory>User prefers Python.</memory>\n</system-reminder>",
additional_kwargs={_DYNAMIC_CONTEXT_REMINDER_KEY: True},
),
HumanMessage(content="请帮我写测试"),
AIMessage(content="好的"),
]
}
prompt, user_msg = middleware._build_title_prompt(state)
assert user_msg == "请帮我写测试"
assert "<system-reminder>" not in prompt
assert "User prefers Python" not in prompt
def test_generate_title_async_strips_think_tags_in_response(self, monkeypatch):
"""Async title generation strips <think> blocks from the model response."""
_set_test_title_config(max_chars=50)