Skip to content

Commit 5275043

Browse files
author
SolonCode
committed
移除循环执行历史记录功能,简化状态管理
1 parent b73f686 commit 5275043

3 files changed

Lines changed: 8 additions & 138 deletions

File tree

soloncode-cli/src/main/java/org/noear/solon/codecli/command/builtin/LoopScheduler.java

Lines changed: 6 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -486,7 +486,7 @@ private void onTrigger(String sessionId, LoopTask task) {
486486
}
487487

488488
try {
489-
// ③ 执行一轮(含 prompt 构建、AI 调用、状态评估、历史写入、持久化)
489+
// ③ 执行一轮(含 prompt 构建、AI 调用、状态评估、持久化)
490490
GoalRoundOutcome outcome = executeGoalRound(sessionId, task);
491491

492492
// ④ 事件驱动续行:仅 CONTINUE 且 goal 仍活跃时 submit 下一轮
@@ -537,13 +537,8 @@ private boolean checkGuardConditions(String sessionId, LoopTask task) {
537537
task.getId(), elapsed, maxDurationMs);
538538
executeBudgetLimitWrapUp(sessionId, task, gs);
539539

540-
if (gs.getStatus() == GoalState.Status.ACHIEVED) {
541-
LoopStateManager.appendHistory(engine.getWorkspace(), task.getId(),
542-
(String) null, task.getCurrentIteration(), "GOAL_ACHIEVED");
543-
} else {
540+
if (gs.getStatus() != GoalState.Status.ACHIEVED) {
544541
gs.markBudgetLimited();
545-
LoopStateManager.appendHistory(engine.getWorkspace(), task.getId(),
546-
(String) null, task.getCurrentIteration(), "BUDGET_EXCEEDED");
547542
}
548543

549544
disableGoalScheduling(sessionId, task);
@@ -557,13 +552,8 @@ private boolean checkGuardConditions(String sessionId, LoopTask task) {
557552
task.getId(), task.getCurrentIteration());
558553
executeBudgetLimitWrapUp(sessionId, task, gs);
559554

560-
if (gs.getStatus() == GoalState.Status.ACHIEVED) {
561-
LoopStateManager.appendHistory(engine.getWorkspace(), task.getId(),
562-
(String) null, task.getCurrentIteration(), "GOAL_ACHIEVED");
563-
} else {
555+
if (gs.getStatus() != GoalState.Status.ACHIEVED) {
564556
gs.markBudgetLimited();
565-
LoopStateManager.appendHistory(engine.getWorkspace(), task.getId(),
566-
(String) null, task.getCurrentIteration(), "BUDGET_EXCEEDED");
567557
}
568558

569559
disableGoalScheduling(sessionId, task);
@@ -580,7 +570,7 @@ private boolean checkGuardConditions(String sessionId, LoopTask task) {
580570
}
581571

582572
/**
583-
* 执行单轮 Goal 调用(含 prompt 构建、AI 执行、状态评估、历史写入、持久化)
573+
* 执行单轮 Goal 调用(含 prompt 构建、AI 执行、状态评估、持久化)
584574
*
585575
* <p>返回 GoalRoundOutcome 枚举,供调用方决定是否续行。
586576
*/
@@ -626,8 +616,6 @@ private GoalRoundOutcome executeGoalRound(String sessionId, LoopTask task) {
626616
if (achieved) {
627617
LOG.info("Loop task '{}' goal ACHIEVED at iteration {}", task.getId(), iteration);
628618
gs.achieve();
629-
LoopStateManager.appendHistory(engine.getWorkspace(), task.getId(),
630-
executionResult, iteration, "GOAL_ACHIEVED");
631619
disableGoalScheduling(sessionId, task);
632620
return GoalRoundOutcome.ACHIEVED;
633621
}
@@ -640,23 +628,17 @@ private GoalRoundOutcome executeGoalRound(String sessionId, LoopTask task) {
640628

641629
// wrap-up 回合若 LLM 认为目标已达成,则标记 ACHIEVED 而非 BUDGET_EXCEEDED
642630
if (gs.getStatus() == GoalState.Status.ACHIEVED) {
643-
LoopStateManager.appendHistory(engine.getWorkspace(), task.getId(),
644-
executionResult, iteration, "GOAL_ACHIEVED");
645631
disableGoalScheduling(sessionId, task);
646632
return GoalRoundOutcome.ACHIEVED;
647633
} else {
648634
gs.markBudgetLimited();
649-
LoopStateManager.appendHistory(engine.getWorkspace(), task.getId(),
650-
executionResult, iteration, "BUDGET_EXCEEDED");
651635
disableGoalScheduling(sessionId, task);
652636
return GoalRoundOutcome.BUDGET_EXCEEDED;
653637
}
654638
}
655639
}
656640

657-
// 写入执行历史
658-
LoopStateManager.appendHistory(engine.getWorkspace(), task.getId(),
659-
executionResult, iteration, "NONE");
641+
660642

661643
// 实时持久化
662644
saveToFile(sessionId, sessionTasks.get(sessionId));
@@ -713,8 +695,7 @@ private void handleExecutionError(String sessionId, LoopTask task, Exception e)
713695
task.getId(), errors);
714696
gs.markBlocked();
715697
pauseGoal(sessionId, task.getId());
716-
LoopStateManager.appendHistory(engine.getWorkspace(), task.getId(),
717-
(String) null, task.getCurrentIteration(), "BLOCKED_BY_ERRORS");
698+
718699
} else {
719700
// 未达阈值 → 递增延迟重试
720701
long delay = 5L * errors; // 5s, 10s, 15s ...

soloncode-cli/src/main/java/org/noear/solon/codecli/command/builtin/LoopStateManager.java

Lines changed: 2 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -15,35 +15,21 @@
1515
*/
1616
package org.noear.solon.codecli.command.builtin;
1717

18-
import org.noear.snack4.Feature;
19-
import org.noear.snack4.ONode;
20-
import org.noear.snack4.Options;
2118
import org.noear.solon.codecli.config.AgentFlags;
2219
import org.slf4j.Logger;
2320
import org.slf4j.LoggerFactory;
2421

25-
import java.io.OutputStreamWriter;
26-
import java.io.Writer;
27-
import java.nio.charset.StandardCharsets;
2822
import java.nio.file.*;
29-
import java.time.Instant;
3023

3124
/**
32-
* Loop 状态管理器 — 负责 .soloncode/loops/&lt;loopId&gt;/ 目录的创建、读写、清理。
33-
*
34-
* <p>状态目录结构:
35-
* <pre>
36-
* .soloncode/loops/&lt;loopId&gt;/
37-
* └── history.json # 结构化执行历史
38-
* </pre>
25+
* Loop 状态管理器 — 负责 .soloncode/loops/&lt;loopId&gt;/ 目录的创建、清理。
3926
*
4027
* @author noear
4128
* @since 3.9.1
4229
*/
4330
public class LoopStateManager {
4431
private static final Logger LOG = LoggerFactory.getLogger(LoopStateManager.class);
4532

46-
private static final String HISTORY_FILE = "history.json";
4733
/**
4834
* 获取 loop 状态目录的根路径(.soloncode/loops/)
4935
*/
@@ -59,7 +45,7 @@ public static Path getStateDir(String workspace, String loopId) {
5945
}
6046

6147
/**
62-
* 初始化状态目录(创建目录和 history.json)
48+
* 初始化状态目录
6349
*
6450
* @return 状态目录路径
6551
*/
@@ -68,72 +54,13 @@ public static String init(String workspace, String loopId, String prompt) {
6854
try {
6955
Files.createDirectories(stateDir);
7056

71-
// 创建空的 history.json
72-
if (!Files.exists(stateDir.resolve(HISTORY_FILE))) {
73-
writeFile(stateDir.resolve(HISTORY_FILE), "[]");
74-
}
75-
7657
return stateDir.toString();
7758
} catch (Exception e) {
7859
LOG.warn("Failed to init loop state dir '{}': {}", stateDir, e.getMessage());
7960
return stateDir.toString();
8061
}
8162
}
8263

83-
/**
84-
* 追加一条执行历史
85-
*/
86-
public static void appendHistory(String workspace, String loopId, String result, int iteration) {
87-
appendHistory(workspace, loopId, result, iteration, "NONE");
88-
}
89-
90-
/**
91-
* 追加一条执行历史
92-
*/
93-
public static void appendHistory(String workspace, String loopId, String result, int iteration, String stopReason) {
94-
appendHistory(workspace, loopId, LoopExecutionResult.fromText(result), iteration, stopReason);
95-
}
96-
97-
/**
98-
* 追加一条结构化执行历史(含 Goal 状态)
99-
*/
100-
public static void appendHistory(String workspace, String loopId, LoopExecutionResult result, int iteration, String stopReason) {
101-
try {
102-
Path historyFile = getStateDir(workspace, loopId).resolve(HISTORY_FILE);
103-
if (!Files.exists(historyFile)) {
104-
writeFile(historyFile, "[]");
105-
}
106-
107-
String json = new String(Files.readAllBytes(historyFile), StandardCharsets.UTF_8);
108-
ONode root = ONode.ofJson(json,Feature.Write_PrettyFormat);
109-
if (!root.isArray()) {
110-
root = ONode.ofJson("[]",Feature.Write_PrettyFormat);
111-
}
112-
113-
ONode entry = new ONode();
114-
entry.set("iteration", iteration);
115-
entry.set("time", Instant.now().toString());
116-
entry.set("result", result != null && result.getFinalResult() != null ? result.getFinalResult() : "ok");
117-
if (result != null) {
118-
entry.set("submitted", result.isSubmitted());
119-
entry.set("completed", result.isCompleted());
120-
entry.set("goalAchieved", result.isGoalAchieved());
121-
if (result.getErrorMessage() != null) entry.set("error", result.getErrorMessage());
122-
}
123-
entry.set("stopReason", stopReason != null ? stopReason : "NONE");
124-
125-
// ★ P0: 记录 goal 状态(如果有)
126-
// 由于这里没有 LoopTask 引用,goal 状态由调用方在 stopReason 中体现
127-
// 例如:"GOAL_ACHIEVED", "BUDGET_LIMITED", "MAX_ITERATIONS_REACHED"
128-
129-
root.add(entry);
130-
131-
writeFile(historyFile, root.toJson());
132-
} catch (Exception e) {
133-
LOG.warn("Failed to append history for loop '{}': {}", loopId, e.getMessage());
134-
}
135-
}
136-
13764
/**
13865
* 清理状态目录
13966
*/
@@ -154,16 +81,4 @@ public static void cleanup(String workspace, String loopId) {
15481
LOG.warn("Failed to cleanup loop state '{}': {}", loopId, e.getMessage());
15582
}
15683
}
157-
158-
// ==================== 内部工具方法 ====================
159-
160-
private static void writeFile(Path file, String content) throws Exception {
161-
Path tempFile = file.resolveSibling(file.getFileName() + ".tmp");
162-
try (Writer w = new OutputStreamWriter(Files.newOutputStream(tempFile,
163-
StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING),
164-
StandardCharsets.UTF_8)) {
165-
w.write(content);
166-
}
167-
Files.move(tempFile, file, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE);
168-
}
16984
}

soloncode-cli/src/test/java/org/noear/solon/codecli/command/builtin/LoopExecutionResultTest.java

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,6 @@
1616
package org.noear.solon.codecli.command.builtin;
1717

1818
import org.junit.jupiter.api.Test;
19-
import org.noear.snack4.ONode;
20-
21-
import java.nio.file.Files;
22-
import java.nio.file.Path;
2319

2420
import static org.junit.jupiter.api.Assertions.*;
2521

@@ -54,29 +50,7 @@ void copyWithUpdateShouldKeepIdentityAndRuntimeState() {
5450

5551
}
5652

57-
@Test
58-
void appendHistoryShouldWriteStructuredResult() throws Exception {
59-
Path workspace = Files.createTempDirectory("loop-state-test-");
60-
LoopTask task = new LoopTask("prompt", 1, null, LoopTask.TaskType.GOAL, false);
61-
62-
LoopStateManager.init(workspace.toString(), task.getId(), task.getPrompt());
63-
LoopStateManager.appendHistory(workspace.toString(), task.getId(),
64-
LoopExecutionResult.fromText("done\n[GOAL_ACHIEVED]"),
65-
1, "GOAL_ACHIEVED");
6653

67-
String json = new String(Files.readAllBytes(workspace.resolve(".soloncode").resolve("loops")
68-
.resolve(task.getId()).resolve("history.json")), "UTF-8");
69-
ONode root = ONode.ofJson(json);
70-
71-
assertTrue(root.isArray());
72-
int count = 0;
73-
for (ONode ignored : root.getArray()) {
74-
count++;
75-
}
76-
assertEquals(1, count);
77-
assertEquals("GOAL_ACHIEVED", root.get(0).get("stopReason").getString());
78-
assertTrue(root.get(0).get("goalAchieved").getBoolean());
79-
}
8054

8155
@Test
8256
void submittedOnlyShouldNotBeGoalAchieved() {

0 commit comments

Comments
 (0)