fix(session): stop training the model to parrot [Message Time: ...]

Past assistant turns in history were prefixed with "[Message Time: ...]"
just like user turns. The model treated these as in-context demos and
started prefixing its own replies with the same marker, leaking
metadata to the user. Prompt-level warnings could not beat dozens of
prior assistant samples.

Annotate only user turns and proactive deliveries
(_channel_delivery=True, i.e. cron / heartbeat pushes whose timing is
the whole point and which are too infrequent to act as demos). Adjacent
user-side timestamps still pin every normal assistant reply for
relative-time reasoning. The now-redundant identity.md warning is
removed along with the demonstration source.
This commit is contained in:
Xubin Ren
2026-04-27 07:11:20 +00:00
parent 620d9e4f31
commit 311a7fe36e
5 changed files with 72 additions and 16 deletions
+20 -6
View File
@@ -32,13 +32,27 @@ class Session:
@staticmethod
def _annotate_message_time(message: dict[str, Any], content: Any) -> Any:
"""Expose persisted turn timestamps to the model for relative-date reasoning."""
"""Expose persisted turn timestamps to the model for relative-date reasoning.
Annotating *every* assistant turn trains the model (via in-context
demonstrations) to start its own replies with the same
``[Message Time: ...]`` prefix, which leaks metadata back to the user.
We therefore only annotate:
* ``user`` turns — needed so the model can pin the conversation in time.
* proactive deliveries (``_channel_delivery=True``) — cron / heartbeat
assistant pushes that may sit hours away from the next user reply,
and are too infrequent to act as parroting demonstrations.
"""
timestamp = message.get("timestamp")
if (
not timestamp
or message.get("role") not in {"user", "assistant"}
or not isinstance(content, str)
):
if not timestamp or not isinstance(content, str):
return content
role = message.get("role")
if role == "user":
pass
elif role == "assistant" and message.get("_channel_delivery"):
pass
else:
return content
return f"[Message Time: {timestamp}]\n{content}"
-4
View File
@@ -28,9 +28,5 @@ Output is rendered in a terminal. Avoid markdown headings and tables. Use plain
- On broad searches, use `grep(output_mode="count")` to scope before requesting full content.
{% include 'agent/_snippets/untrusted_content.md' %}
Historical messages may include `[Message Time: ...]` prefixes. Treat them as
metadata for chronology only; never quote, copy, or include those markers in
your response.
Reply directly with text for conversations. Only use the 'message' tool to send to a specific chat channel.
IMPORTANT: To send files (images, video, audio, documents) to the user, you MUST call the 'message' tool with the 'media' parameter. Do NOT use read_file to "send" a file — reading a file only shows its content to you, it does NOT deliver the file to the user. Examples: message(content="Here is the image", media=["/path/to/file.png"]) or message(content="Here is the video", media=["/path/to/video.mp4"])