mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-08-09 12:40:59 +00:00
fix(pipeline): ground local-agent system prompt with current date (#2399)
The local-agent runner's system prompt is a static string with no template-variable support, so the model had no anchor for "today" and resolved relative time references (e.g. "this quarter", "latest") against whichever period was best represented in training data instead of the real date, sometimes confidently answering with stale information for time-sensitive questions. PreProcessor now appends a short, deterministically-computed "Current date: ..." note to the system prompt on every request for local-agent pipelines, alongside guidance to verify time-sensitive facts with a search tool rather than answering from memory. The existing skill-awareness prompt injection is refactored to share the same append-to-system-prompt helper.
This commit is contained in:
@@ -488,3 +488,115 @@ class TestPreProcessorToolSelection:
|
||||
result = await stage.process(query, 'PreProcessor')
|
||||
|
||||
assert [tool.name for tool in result.new_query.use_funcs] == ['plugin_tool']
|
||||
|
||||
|
||||
class TestPreProcessorDateGrounding:
|
||||
"""Tests for current-date injection into the local-agent system prompt."""
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_local_agent_appends_date_to_existing_system_message(self):
|
||||
"""Date grounding text should be appended to an existing system prompt."""
|
||||
preproc = get_preproc_module()
|
||||
|
||||
app = FakeApp()
|
||||
mock_session = make_session()
|
||||
app.sess_mgr.get_session = AsyncMock(return_value=mock_session)
|
||||
|
||||
mock_conversation = Mock()
|
||||
mock_conversation.prompt = Mock(messages=[])
|
||||
mock_conversation.prompt.copy = Mock(return_value=Mock(messages=[]))
|
||||
mock_conversation.messages = []
|
||||
mock_conversation.uuid = None
|
||||
app.sess_mgr.get_conversation = AsyncMock(return_value=mock_conversation)
|
||||
|
||||
app.model_mgr.get_model_by_uuid = AsyncMock(return_value=None)
|
||||
app.tool_mgr.get_all_tools = AsyncMock(return_value=[])
|
||||
|
||||
from langbot_plugin.api.entities.builtin.provider import message as provider_message
|
||||
|
||||
system_message = provider_message.Message(role='system', content='You are a helpful assistant.')
|
||||
mock_event_ctx = Mock()
|
||||
mock_event_ctx.event = Mock(default_prompt=[system_message], prompt=[])
|
||||
app.plugin_connector.emit_event = AsyncMock(return_value=mock_event_ctx)
|
||||
|
||||
stage = preproc.PreProcessor(app)
|
||||
query = text_query('hello')
|
||||
|
||||
result = await stage.process(query, 'PreProcessor')
|
||||
|
||||
messages = result.new_query.prompt.messages
|
||||
assert len(messages) == 1
|
||||
assert messages[0].role == 'system'
|
||||
assert messages[0].content.startswith('You are a helpful assistant.')
|
||||
assert 'Current date:' in messages[0].content
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_local_agent_creates_system_message_when_none_exists(self):
|
||||
"""A system message should be created when the prompt has none."""
|
||||
preproc = get_preproc_module()
|
||||
|
||||
app = FakeApp()
|
||||
mock_session = make_session()
|
||||
app.sess_mgr.get_session = AsyncMock(return_value=mock_session)
|
||||
|
||||
mock_conversation = Mock()
|
||||
mock_conversation.prompt = Mock(messages=[])
|
||||
mock_conversation.prompt.copy = Mock(return_value=Mock(messages=[]))
|
||||
mock_conversation.messages = []
|
||||
mock_conversation.uuid = None
|
||||
app.sess_mgr.get_conversation = AsyncMock(return_value=mock_conversation)
|
||||
|
||||
app.model_mgr.get_model_by_uuid = AsyncMock(return_value=None)
|
||||
app.tool_mgr.get_all_tools = AsyncMock(return_value=[])
|
||||
|
||||
mock_event_ctx = Mock()
|
||||
mock_event_ctx.event = Mock(default_prompt=[], prompt=[])
|
||||
app.plugin_connector.emit_event = AsyncMock(return_value=mock_event_ctx)
|
||||
|
||||
stage = preproc.PreProcessor(app)
|
||||
query = text_query('hello')
|
||||
|
||||
result = await stage.process(query, 'PreProcessor')
|
||||
|
||||
messages = result.new_query.prompt.messages
|
||||
assert len(messages) == 1
|
||||
assert messages[0].role == 'system'
|
||||
assert 'Current date:' in messages[0].content
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_non_local_agent_runner_skips_date_injection(self):
|
||||
"""Runners other than local-agent should not get the date addition."""
|
||||
preproc = get_preproc_module()
|
||||
|
||||
app = FakeApp()
|
||||
mock_session = make_session()
|
||||
app.sess_mgr.get_session = AsyncMock(return_value=mock_session)
|
||||
|
||||
mock_conversation = Mock()
|
||||
mock_conversation.prompt = Mock(messages=[])
|
||||
mock_conversation.prompt.copy = Mock(return_value=Mock(messages=[]))
|
||||
mock_conversation.messages = []
|
||||
mock_conversation.uuid = None
|
||||
app.sess_mgr.get_conversation = AsyncMock(return_value=mock_conversation)
|
||||
|
||||
app.model_mgr.get_model_by_uuid = AsyncMock(return_value=None)
|
||||
app.tool_mgr.get_all_tools = AsyncMock(return_value=[])
|
||||
|
||||
mock_event_ctx = Mock()
|
||||
mock_event_ctx.event = Mock(default_prompt=[], prompt=[])
|
||||
app.plugin_connector.emit_event = AsyncMock(return_value=mock_event_ctx)
|
||||
|
||||
stage = preproc.PreProcessor(app)
|
||||
query = text_query('hello')
|
||||
query.pipeline_config = {
|
||||
'ai': {
|
||||
'runner': {'runner': 'dify-service-api'},
|
||||
'local-agent': {'model': {'primary': '', 'fallbacks': []}, 'prompt': 'default'},
|
||||
},
|
||||
'output': {'misc': {'at-sender': False}},
|
||||
'trigger': {'misc': {}},
|
||||
}
|
||||
|
||||
result = await stage.process(query, 'PreProcessor')
|
||||
|
||||
assert result.new_query.prompt.messages == []
|
||||
|
||||
Reference in New Issue
Block a user