Skip to content

Commit 7884b54

Browse files
committed
Refine supervisor evaluation prompt
1 parent 135bd23 commit 7884b54

5 files changed

Lines changed: 92 additions & 21 deletions

File tree

packages/core/src/domain/supervisor.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,7 @@ export type CycleTrigger = "turn_completed" | "manual" | "scheduled";
2222
export type SupervisorStopReason =
2323
| "objective_complete"
2424
| "max_supervision_count_reached"
25-
| "supervisor_uncertain"
26-
| "needs_user_input";
25+
| "supervisor_uncertain";
2726

2827
export type SupervisorPlanStepStatus = "pending" | "in_progress" | "done";
2928

@@ -55,7 +54,7 @@ export interface SupervisorCycleTargetRecord {
5554
startedAt: number;
5655
completedAt: number;
5756
result: "continue" | "stop" | "error";
58-
stopReason?: "objective_complete" | "supervisor_uncertain" | "needs_user_input";
57+
stopReason?: "objective_complete" | "supervisor_uncertain";
5958
reason?: string;
6059
guidance?: string;
6160
progressSummary?: string;

packages/server/src/supervisor/evaluator.test.ts

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,21 @@ describe("SupervisorEvaluator", () => {
293293
});
294294
});
295295

296+
it("rejects needs_user_input as a stopReason", async () => {
297+
const evaluator = makeEvaluator(
298+
JSON.stringify({
299+
status: "stop",
300+
stopReason: "needs_user_input",
301+
reason: "Need user input",
302+
}),
303+
"claude"
304+
);
305+
306+
await expect(evaluator.evaluate(makeSupervisor("claude"), makeContext())).rejects.toThrow(
307+
"Supervisor stop result is missing a valid stopReason"
308+
);
309+
});
310+
296311
it("falls back to provider.defaultConfig when evaluator config is missing", async () => {
297312
const evaluator = new SupervisorEvaluator({
298313
providerRegistry: [
@@ -434,8 +449,23 @@ describe("SupervisorEvaluator", () => {
434449
).rejects.toThrow();
435450

436451
const prompt = (logger.warn.mock.calls[0]?.[0] as { prompt?: string } | undefined)?.prompt;
437-
expect(prompt).toContain("You are supervising a target-scoped software task.");
452+
expect(prompt).toContain("You are an autonomous supervisor for a target-scoped software task.");
438453
expect(prompt).toContain("Return JSON only.");
454+
expect(prompt).toContain('Prefer "continue" whenever there is a reasonable next action.');
455+
expect(prompt).toContain(
456+
"Do not ask the user to decide, clarify, or choose among implementation options."
457+
);
458+
expect(prompt).toContain("Use the target memory as the current supervision state.");
459+
expect(prompt).toContain("Identify which plan step is currently active.");
460+
expect(prompt).toContain("If the active step is done, advance to the next useful step.");
461+
expect(prompt).toContain(
462+
"If the agent appears stuck or repeated the same action, give a different concrete next action."
463+
);
464+
expect(prompt).toContain('Use "supervisor_uncertain" only as a last resort');
465+
expect(prompt).toContain('Guidance requirements for "continue":');
466+
expect(prompt).toContain(
467+
"Be specific enough for the supervised agent to act without asking the user."
468+
);
439469
expect(prompt).toContain("Current objective:");
440470
expect(prompt).toContain("Ship the fix");
441471
expect(prompt).toContain("Current target memory:");

packages/server/src/supervisor/evaluator.ts

Lines changed: 57 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,7 @@ const NOOP_LOGGER: FastifyBaseLogger = {
3030

3131
export interface SupervisorEvaluationResult {
3232
status: "continue" | "stop";
33-
stopReason?: Extract<
34-
SupervisorStopReason,
35-
"objective_complete" | "supervisor_uncertain" | "needs_user_input"
36-
>;
33+
stopReason?: Extract<SupervisorStopReason, "objective_complete" | "supervisor_uncertain">;
3734
reason: string;
3835
guidance?: string;
3936
plan?: SupervisorPlanStep[];
@@ -135,20 +132,71 @@ export class SupervisorEvaluator {
135132

136133
function buildPrompt(context: SupervisorEvaluationContext): string {
137134
const lines: string[] = [
138-
"You are supervising a target-scoped software task.",
135+
"You are an autonomous supervisor for a target-scoped software task.",
136+
"Your job is to keep the agent moving toward the objective until the objective is complete.",
137+
"",
139138
"Return JSON only.",
140139
"",
140+
"Decision policy:",
141+
'- Prefer "continue" whenever there is a reasonable next action.',
142+
"- Do not ask the user to decide, clarify, or choose among implementation options.",
143+
"- When information is incomplete, choose a conservative next action based on the objective, target memory, latest user input, and terminal snapshot.",
144+
"- Stop only when the objective is complete, or when continuing would likely push the agent in an unsafe or clearly unsupported direction.",
145+
"",
146+
"Stage decision policy:",
147+
"- Use the target memory as the current supervision state.",
148+
"- Base your decision on the objective, current plan, activeStepId, progressSummary, lastGuidance, stalledCount, latest user input, and terminal snapshot.",
149+
"- Identify which plan step is currently active.",
150+
"- Decide whether the active step is done, still in progress, blocked, or obsolete.",
151+
"- If the active step is done, advance to the next useful step.",
152+
"- If the active step is still in progress, give guidance that moves it forward.",
153+
"- If the agent appears stuck or repeated the same action, give a different concrete next action.",
154+
"- If the plan is obsolete, update only the affected steps unless a full replacement is necessary.",
155+
"",
141156
"Allowed statuses:",
142157
'- "continue": more work is needed; include "reason" and "guidance".',
143158
'- "stop": supervision should stop; include "stopReason" and "reason".',
144159
"",
145160
"Allowed stop reasons:",
146161
'- "objective_complete"',
147162
'- "supervisor_uncertain"',
148-
'- "needs_user_input"',
149163
"",
150-
"If planGenerated is false, bootstrap a plan with 3 to 7 milestone-sized steps.",
151-
"If planGenerated is true, update progress incrementally; do not rewrite the full plan unless absolutely necessary.",
164+
'Use "objective_complete" only when the objective has been satisfied.',
165+
'Use "supervisor_uncertain" only as a last resort when no useful next action can be inferred and additional guidance would likely be misleading.',
166+
"",
167+
'Guidance requirements for "continue":',
168+
"- Give one concrete next action or a short ordered set of concrete actions.",
169+
"- Focus on the highest-value step toward completing the objective.",
170+
"- Be specific enough for the supervised agent to act without asking the user.",
171+
"- Avoid generic reminders, encouragement, or restating the objective.",
172+
"- If verification is needed, tell the agent exactly what to verify next.",
173+
"- If implementation is needed, point to the likely area, behavior, or file/module based on available evidence.",
174+
"",
175+
"Planning policy:",
176+
"- If planGenerated is false, include a plan with 3 to 7 milestone-sized steps.",
177+
"- If planGenerated is true, update progress incrementally.",
178+
"- Do not rewrite the full plan unless the existing plan is clearly wrong or obsolete.",
179+
"- Use stepUpdates to mark completed or active steps when the terminal snapshot shows progress.",
180+
"- Keep activeStepId aligned with the next useful step.",
181+
"",
182+
"Output schema:",
183+
"For continue:",
184+
"{",
185+
' "status": "continue",',
186+
' "reason": "brief explanation of why more work is needed",',
187+
' "guidance": "specific next action for the supervised agent",',
188+
' "plan": optional array of plan steps,',
189+
' "activeStepId": optional step id,',
190+
' "progressSummary": optional brief progress summary,',
191+
' "stepUpdates": optional array of { "id": string, "status": "pending" | "in_progress" | "done" }',
192+
"}",
193+
"",
194+
"For stop:",
195+
"{",
196+
' "status": "stop",',
197+
' "stopReason": "objective_complete" | "supervisor_uncertain",',
198+
' "reason": "brief explanation"',
199+
"}",
152200
"",
153201
"Current objective:",
154202
context.objective,
@@ -563,11 +611,7 @@ function parseSupervisorEvaluationResult(
563611

564612
if (status === "stop") {
565613
const stopReason = record.stopReason;
566-
if (
567-
stopReason !== "objective_complete" &&
568-
stopReason !== "supervisor_uncertain" &&
569-
stopReason !== "needs_user_input"
570-
) {
614+
if (stopReason !== "objective_complete" && stopReason !== "supervisor_uncertain") {
571615
throw new Error("Supervisor stop result is missing a valid stopReason");
572616
}
573617

packages/web/src/locales/en.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -676,8 +676,7 @@
676676
"stop_reason": {
677677
"objective_complete": "Objective complete. Supervisor stopped automatically.",
678678
"max_supervision_count_reached": "Reached the configured max supervision count.",
679-
"supervisor_uncertain": "Supervisor could not determine the next step and stopped automatic supervision.",
680-
"needs_user_input": "Supervisor needs more user input before automatic supervision can continue."
679+
"supervisor_uncertain": "Supervisor could not determine the next step and stopped automatic supervision."
681680
},
682681
"target_memory": {
683682
"title": "Target memory",

packages/web/src/locales/zh.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -676,8 +676,7 @@
676676
"stop_reason": {
677677
"objective_complete": "目标已达成,Supervisor 已自动停止。",
678678
"max_supervision_count_reached": "已达到配置的最大监督次数。",
679-
"supervisor_uncertain": "Supervisor 暂时无法判断下一步,已停止自动监督。",
680-
"needs_user_input": "Supervisor 需要更多用户输入后才能继续自动监督。"
679+
"supervisor_uncertain": "Supervisor 暂时无法判断下一步,已停止自动监督。"
681680
},
682681
"target_memory": {
683682
"title": "目标记忆",

0 commit comments

Comments
 (0)