mirror of
https://gitee.com/wanwujie/deer-flow
synced 2026-04-15 11:04:44 +08:00
* fix(memory): inject stored facts into system prompt memory context - add Facts section rendering in format_memory_for_injection - rank facts by confidence and coerce confidence values safely - enforce max token budget while appending fact lines - add regression tests for fact inclusion, ordering, and budget behavior Fixes #1059 * Update the document with the latest status * fix(memory): harden fact injection — NaN/inf confidence, None content, incremental token budget (#1090) * Initial plan * fix(memory): address review feedback on confidence coercion, None content, and token budget Co-authored-by: WillemJiang <219644+WillemJiang@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: WillemJiang <219644+WillemJiang@users.noreply.github.com> --------- Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
123 lines
4.5 KiB
Python
123 lines
4.5 KiB
Python
"""Tests for memory prompt injection formatting."""
|
|
|
|
import math
|
|
|
|
from src.agents.memory.prompt import _coerce_confidence, format_memory_for_injection
|
|
|
|
|
|
def test_format_memory_includes_facts_section() -> None:
|
|
memory_data = {
|
|
"user": {},
|
|
"history": {},
|
|
"facts": [
|
|
{"content": "User uses PostgreSQL", "category": "knowledge", "confidence": 0.9},
|
|
{"content": "User prefers SQLAlchemy", "category": "preference", "confidence": 0.8},
|
|
],
|
|
}
|
|
|
|
result = format_memory_for_injection(memory_data, max_tokens=2000)
|
|
|
|
assert "Facts:" in result
|
|
assert "User uses PostgreSQL" in result
|
|
assert "User prefers SQLAlchemy" in result
|
|
|
|
|
|
def test_format_memory_sorts_facts_by_confidence_desc() -> None:
|
|
memory_data = {
|
|
"user": {},
|
|
"history": {},
|
|
"facts": [
|
|
{"content": "Low confidence fact", "category": "context", "confidence": 0.4},
|
|
{"content": "High confidence fact", "category": "knowledge", "confidence": 0.95},
|
|
],
|
|
}
|
|
|
|
result = format_memory_for_injection(memory_data, max_tokens=2000)
|
|
|
|
assert result.index("High confidence fact") < result.index("Low confidence fact")
|
|
|
|
|
|
def test_format_memory_respects_budget_when_adding_facts(monkeypatch) -> None:
|
|
# Make token counting deterministic for this test by counting characters.
|
|
monkeypatch.setattr("src.agents.memory.prompt._count_tokens", lambda text, encoding_name="cl100k_base": len(text))
|
|
|
|
memory_data = {
|
|
"user": {},
|
|
"history": {},
|
|
"facts": [
|
|
{"content": "First fact should fit", "category": "knowledge", "confidence": 0.95},
|
|
{"content": "Second fact should not fit in tiny budget", "category": "knowledge", "confidence": 0.90},
|
|
],
|
|
}
|
|
|
|
first_fact_only_memory_data = {
|
|
"user": {},
|
|
"history": {},
|
|
"facts": [
|
|
{"content": "First fact should fit", "category": "knowledge", "confidence": 0.95},
|
|
],
|
|
}
|
|
one_fact_result = format_memory_for_injection(first_fact_only_memory_data, max_tokens=2000)
|
|
two_facts_result = format_memory_for_injection(memory_data, max_tokens=2000)
|
|
# Choose a budget that can include exactly one fact section line.
|
|
max_tokens = (len(one_fact_result) + len(two_facts_result)) // 2
|
|
|
|
first_only_result = format_memory_for_injection(memory_data, max_tokens=max_tokens)
|
|
|
|
assert "First fact should fit" in first_only_result
|
|
assert "Second fact should not fit in tiny budget" not in first_only_result
|
|
|
|
|
|
def test_coerce_confidence_nan_falls_back_to_default() -> None:
|
|
"""NaN should not be treated as a valid confidence value."""
|
|
result = _coerce_confidence(math.nan, default=0.5)
|
|
assert result == 0.5
|
|
|
|
|
|
def test_coerce_confidence_inf_falls_back_to_default() -> None:
|
|
"""Infinite values should fall back to default rather than clamping to 1.0."""
|
|
assert _coerce_confidence(math.inf, default=0.3) == 0.3
|
|
assert _coerce_confidence(-math.inf, default=0.3) == 0.3
|
|
|
|
|
|
def test_coerce_confidence_valid_values_are_clamped() -> None:
|
|
"""Valid floats outside [0, 1] are clamped; values inside are preserved."""
|
|
assert _coerce_confidence(1.5) == 1.0
|
|
assert _coerce_confidence(-0.5) == 0.0
|
|
assert abs(_coerce_confidence(0.75) - 0.75) < 1e-9
|
|
|
|
|
|
def test_format_memory_skips_none_content_facts() -> None:
|
|
"""Facts with content=None must not produce a 'None' line in the output."""
|
|
memory_data = {
|
|
"facts": [
|
|
{"content": None, "category": "knowledge", "confidence": 0.9},
|
|
{"content": "Real fact", "category": "knowledge", "confidence": 0.8},
|
|
],
|
|
}
|
|
|
|
result = format_memory_for_injection(memory_data, max_tokens=2000)
|
|
|
|
assert "None" not in result
|
|
assert "Real fact" in result
|
|
|
|
|
|
def test_format_memory_skips_non_string_content_facts() -> None:
|
|
"""Facts with non-string content (e.g. int/list) must be ignored."""
|
|
memory_data = {
|
|
"facts": [
|
|
{"content": 42, "category": "knowledge", "confidence": 0.9},
|
|
{"content": ["list"], "category": "knowledge", "confidence": 0.85},
|
|
{"content": "Valid fact", "category": "knowledge", "confidence": 0.7},
|
|
],
|
|
}
|
|
|
|
result = format_memory_for_injection(memory_data, max_tokens=2000)
|
|
|
|
# The formatted line for an integer content would be "- [knowledge | 0.90] 42".
|
|
assert "| 0.90] 42" not in result
|
|
# The formatted line for a list content would be "- [knowledge | 0.85] ['list']".
|
|
assert "| 0.85]" not in result
|
|
assert "Valid fact" in result
|
|
|