feat(agent): echo full ledger snapshot in progress_update tool result

This commit is contained in:
matevip 2026-05-24 23:00:24 +08:00
parent 7736f6b0ab
commit 9f2fb3db23
2 changed files with 25 additions and 4 deletions

View File

@ -70,8 +70,23 @@ public class ProgressLedgerTool {
}
try {
ProgressLedger updated = service.upsert(conversationId, stepKey, label, parsed, note);
return "Recorded " + stepKey + "" + parsed.wireValue()
+ ". Ledger now has " + updated.size() + " entries.";
// Return the freshly-rendered snapshot in the tool result so the
// model immediately sees its own update reflected in the
// canonical view it will be reading next iteration. Without this
// positive-feedback loop the model treats progress_update as a
// fire-and-forget side effect and stops calling it after the
// first few transitions (observed: round-4 dropped to 3 calls in
// 27 minutes of work). The snapshot is also what the runtime
// injects pre-LLM-call, so echoing it here keeps the two views
// identical.
StringBuilder out = new StringBuilder(256);
out.append("✓ Recorded ").append(stepKey).append("").append(parsed.wireValue())
.append(" (").append(updated.size()).append(" entries total).\n\n");
String snapshot = updated.renderSnapshot();
if (snapshot != null) {
out.append(snapshot);
}
return out.toString();
} catch (Exception e) {
log.warn("progress_update failed for conv={} key={}: {}", conversationId, stepKey, e.getMessage());
return "Error: failed to persist progress entry — " + e.getMessage();

View File

@ -37,7 +37,7 @@ class ProgressLedgerToolTest {
}
@Test
@DisplayName("Happy path: valid args persist + return entry count.")
@DisplayName("Happy path: tool result includes the full rendered snapshot for positive feedback.")
void happyPath() {
ToolExecutionContext.set("conv-1", "admin");
ProgressLedgerService service = mock(ProgressLedgerService.class);
@ -51,7 +51,13 @@ class ProgressLedgerToolTest {
ProgressLedgerTool tool = new ProgressLedgerTool(service);
String out = tool.progress_update("step_a", "Step A", "in_progress", "starting now", null);
assertEquals("Recorded step_a → in_progress. Ledger now has 1 entries.", out);
// Header line confirms the write so the model has a clear ack.
assertTrue(out.startsWith("✓ Recorded step_a → in_progress (1 entries total)"), out);
// The rendered snapshot must be appended so the model sees its own
// update reflected in the same view the runtime injects each turn.
assertTrue(out.contains("当前任务进度"), "expected snapshot in tool result: " + out);
assertTrue(out.contains("Step A"), "expected entry label in snapshot: " + out);
assertTrue(out.contains("`step_a`"), "expected bracketed key in snapshot: " + out);
verify(service, times(1)).upsert(any(), any(), any(), any(), any());
}