mirror of
https://github.com/bytedance/deer-flow.git
synced 2026-10-26 21:26:39 +00:00
fix(agents): strip a leading BOM from SOUL.md before prompt injection (#7061)
Co-authored-by: Jay0130-a <Jay0130-a@users.noreply.github.com>
This commit is contained in:
parent
d1d0958bea
commit
dfb32ef52b
@ -377,7 +377,11 @@ def load_agent_soul(agent_name: str | None, *, user_id: str | None = None) -> st
|
||||
soul_path = get_paths().base_dir / SOUL_FILENAME
|
||||
if not soul_path.exists():
|
||||
return None
|
||||
content = soul_path.read_text(encoding="utf-8").strip()
|
||||
# ``utf-8-sig`` drops the BOM a Windows editor (Notepad, PowerShell 5.1)
|
||||
# prepends. ``str.strip()`` does not remove U+FEFF, so reading this as
|
||||
# plain ``utf-8`` would make the mark the first character of the text
|
||||
# injected into the lead agent's system prompt.
|
||||
content = soul_path.read_text(encoding="utf-8-sig").strip()
|
||||
return content or None
|
||||
from deerflow.persistence.agents import get_agent_store
|
||||
|
||||
|
||||
@ -83,7 +83,11 @@ class FileAgentStore(AgentStore):
|
||||
break
|
||||
if not soul_path.exists():
|
||||
return None
|
||||
content = soul_path.read_text(encoding="utf-8").strip()
|
||||
# Match the default-agent read in :func:`deerflow.config.agents_config.
|
||||
# load_agent_soul`: ``utf-8-sig`` drops the BOM a Windows editor writes,
|
||||
# which ``str.strip()`` would otherwise leave as the leading character of
|
||||
# the ``<soul>`` block.
|
||||
content = soul_path.read_text(encoding="utf-8-sig").strip()
|
||||
return content or None
|
||||
|
||||
def list(self, *, user_id: str | None = None) -> list[AgentConfig]:
|
||||
|
||||
@ -323,6 +323,37 @@ class TestLoadAgentSoul:
|
||||
|
||||
assert soul == expected_soul
|
||||
|
||||
def test_strips_a_leading_bom_from_named_agent_soul(self, tmp_path):
|
||||
"""A SOUL.md saved by a Windows editor keeps a UTF-8 BOM.
|
||||
|
||||
``str.strip()`` does not remove U+FEFF, so without normalization the mark
|
||||
becomes the first character of the text injected into the lead agent's
|
||||
``<soul>`` block. SOUL.md is edited outside DeerFlow (Notepad, PowerShell
|
||||
5.1), which is exactly where a BOM comes from.
|
||||
"""
|
||||
expected_soul = "You are a specialized code review expert."
|
||||
_write_agent(tmp_path, "code-reviewer", {"name": "code-reviewer"}, soul="\ufeff" + expected_soul)
|
||||
|
||||
with patch("deerflow.config.agents_config.get_paths", return_value=_make_paths(tmp_path)):
|
||||
from deerflow.config.agents_config import AgentConfig, load_agent_soul
|
||||
|
||||
cfg = AgentConfig(name="code-reviewer")
|
||||
soul = load_agent_soul(cfg.name)
|
||||
|
||||
assert soul == expected_soul
|
||||
|
||||
def test_strips_a_leading_bom_from_the_default_agent_soul(self, tmp_path):
|
||||
"""The default agent reads ``{base_dir}/SOUL.md`` directly — a second read site."""
|
||||
expected_soul = "You are the default agent."
|
||||
(tmp_path / "SOUL.md").write_text("\ufeff" + expected_soul, encoding="utf-8")
|
||||
|
||||
with patch("deerflow.config.agents_config.get_paths", return_value=_make_paths(tmp_path)):
|
||||
from deerflow.config.agents_config import load_agent_soul
|
||||
|
||||
soul = load_agent_soul(None)
|
||||
|
||||
assert soul == expected_soul
|
||||
|
||||
def test_missing_soul_file_returns_none(self, tmp_path):
|
||||
agent_dir = tmp_path / "agents" / "no-soul"
|
||||
agent_dir.mkdir(parents=True)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user