[eric] workflows: a test that runs long hands back what it has instead of telling the agent to poll; memory stops re-deriving facts it already stored

This commit is contained in:
ciregenz
2026-08-07 17:33:20 -07:00
parent bde92de062
commit 9b54b39dde
2 changed files with 30 additions and 5 deletions
+18 -4
View File
@@ -546,7 +546,10 @@ def handle_delete_step(args: dict) -> dict:
# How long a synchronous test may hold the turn. Long enough for a real multi-step workflow, short
# enough that a wedged test returns an honest "still running" instead of hanging the conversation.
TEST_WAIT_S = 240
# A real workflow test does several searches and page fetches per step. The first budget was 240s
# and a live three-step digest took 243, so it missed by three seconds and the agent had to make a
# second hop anyway, which is the exact re-pinging this tool exists to remove.
TEST_WAIT_S = 600
TEST_POLL_S = 3
@@ -574,10 +577,21 @@ def handle_test_workflow(args: dict) -> dict:
continue
transcript = t.get("transcript") or "(empty transcript)"
return _ok(f"Test finished (status: {last_status}). Transcript:\n\n{transcript}")
return _ok(
f"Test Agent (session {sid[:8]}) is still running after {TEST_WAIT_S}s, so it is a long one. "
f"Last status: {last_status}. Call ReadTestTranscript to pick up the result."
# Hand back whatever the run has actually produced. Returning only "call ReadTestTranscript" made
# the model guess when to poll, which is the same dead end as not waiting at all; a partial
# transcript is something it can reason about right now.
partial = ""
try:
t = _call("GET", f"/{wid}/test-transcript")
if "_error" not in t:
partial = (t.get("transcript") or "").strip()
except Exception:
partial = ""
head = (
f"Test Agent (session {sid[:8]}) has not finished after {TEST_WAIT_S}s (status: {last_status}). "
"Everything it has produced so far follows; call ReadTestTranscript for the rest once it lands."
)
return _ok(f"{head}\n\n{partial}" if partial else head)
def handle_read_test_transcript(args: dict) -> dict:
+12 -1
View File
@@ -11,6 +11,7 @@ from backend.apps.agents.core.aux_llm import aux_max_tokens_for
from backend.apps.agents.core.models import AgentSession
from backend.apps.agents.manager.predict_followups import conversation_tail
from backend.apps.agents.manager.session.history_compaction import get_branch_messages
from backend.apps.memory import store
from backend.apps.memory.store import add_fact
logger = logging.getLogger(__name__)
@@ -65,7 +66,17 @@ async def distill_session_memory(session: AgentSession) -> List[str]:
f"Return at most {MAX_FACTS_PER_DISTILL} facts, one per line, no numbering, no quotes. "
"If the conversation reveals nothing durable, return the single word NOTHING."
)
user_turn = "Conversation:\n<transcript>\n" + tail + "\n</transcript>\n\nExtract the facts."
# Show it what is already known. Without this the model re-derives facts it recorded weeks
# ago, phrased differently every time, and the store's token-overlap guard cannot catch a
# paraphrase: the four duplicate pairs found in a real memory scored 0.11 to 0.33 against a
# 0.60 threshold. The generator is semantic, so the dedupe has to be too.
known = "\n".join(f"- {f.text}" for f in store.list_facts())
already = (
"Facts already stored (do NOT repeat these, in any wording):\n" + known + "\n\n"
"Return a fact ONLY if it is genuinely new, or is strictly more specific than one above "
"(in which case return the sharper version and it will replace the old one).\n\n"
) if known else ""
user_turn = already + "Conversation:\n<transcript>\n" + tail + "\n</transcript>\n\nExtract the facts."
chunks: List[str] = []
async with client.messages.stream(
model=aux_model,