Skip to content

Commit d860fea

Browse files
Copilotphrocker
andauthored
Improve planning semantics with plan state tracking and autonomous agent support in enterprise-agent (#177)
* Initial plan * Improve planning semantics with plan state tracking and conversational handling Co-authored-by: phrocker <[email protected]> * Address code review: add helper method, optimize operations tracking, add infinite loop prevention Co-authored-by: phrocker <[email protected]> * Address remaining code review comments: fix awaiting_input handling, imports, null checks Co-authored-by: phrocker <[email protected]> * Fix context duplication and add autonomous agent mode support Co-authored-by: phrocker <[email protected]> * Make isAutonomous parameter explicit in buildStrictModeProtocol calls for consistency Co-authored-by: phrocker <[email protected]> * fix --------- Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: phrocker <[email protected]> Co-authored-by: Marc Parisi <[email protected]>
1 parent 2067fae commit d860fea

File tree

5 files changed

+771
-142
lines changed

5 files changed

+771
-142
lines changed

enterprise-agent/src/main/java/io/sentrius/agent/analysis/agents/agents/ChatAgent.java

Lines changed: 78 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@ public class ChatAgent extends BaseEnterpriseAgent {
5454
private volatile boolean paused = false;
5555
private final Object pauseLock = new Object();
5656
private Thread workerThread;
57+
58+
/** Maximum number of consecutive conversational-only responses before waiting for user input */
59+
private static final int MAX_CONSECUTIVE_CONVERSATIONAL = 5;
5760

5861
private AgentExecution agentExecution;
5962

@@ -169,11 +172,15 @@ public void onApplicationEvent(final ApplicationReadyEvent event) {
169172
}
170173
PromptBuilder promptBuilder = new PromptBuilder(verbRegistry, config);
171174
var prompt = promptBuilder.buildPrompt(false);
175+
// Check if this is an autonomous agent
176+
boolean isAutonomous = null != agentConfigOptions.getType() &&
177+
agentConfigOptions.getType().equalsIgnoreCase("chat-autonomous");
178+
172179
try {
173-
if (null != agentConfigOptions.getType() && agentConfigOptions.getType().equalsIgnoreCase("chat" +
174-
"-autonomous")) {
175-
176-
response = chatVerbs.promptAgent(agentExecution, agentExecutionContext, prompt);
180+
if (isAutonomous) {
181+
// Pass isAutonomous=true to get autonomous-mode prompting
182+
response = chatVerbs.promptAgent(agentExecution, agentExecutionContext, prompt,
183+
new ArrayList<>(), "idle", true);
177184
}
178185
} catch (ZtatException e) {
179186
throw new RuntimeException(e);
@@ -182,18 +189,25 @@ public void onApplicationEvent(final ApplicationReadyEvent event) {
182189
}
183190

184191

185-
if (null != agentConfigOptions.getType() && agentConfigOptions.getType().equalsIgnoreCase("chat-autonomous") && response == null) {
192+
if (isAutonomous && response == null) {
186193
log.error("Chat autonomous agent mode enabled but no response received from promptAgent, shutting down...");
187194
throw new RuntimeException("Chat autonomous agent mode enabled but no response received from promptAgent");
188195
}
189196
VerbResponse lastVerbResponse = null;
190197
LLMResponse nextResponse = null;
191198
List<VerbResponse> verbResponses = new ArrayList<>();
199+
200+
// Track executed operations across the session for plan state awareness
201+
List<String> executedOperations = new ArrayList<>();
202+
String currentPlanStatus = "idle";
203+
204+
// Track consecutive conversational-only responses to prevent infinite loops
205+
int consecutiveConversationalResponses = 0;
206+
192207
while(running) {
193208

194209
// Check if agent is paused if autonomous mode
195-
if (null != agentConfigOptions.getType() && agentConfigOptions.getType().equalsIgnoreCase("chat" +
196-
"-autonomous")) {
210+
if (isAutonomous) {
197211
synchronized (pauseLock) {
198212
while (paused) {
199213
try {
@@ -213,14 +227,14 @@ public void onApplicationEvent(final ApplicationReadyEvent event) {
213227

214228
Thread.sleep(5_000);
215229
agentClientService.heartbeat(agentExecution, agentExecution.getUser().getUsername());
216-
if (null != agentConfigOptions.getType() && agentConfigOptions.getType().equalsIgnoreCase("chat" +
217-
"-autonomous")) {
230+
if (isAutonomous) {
218231
log.info("Chat autonomous agent mode enabled, executing workload...");
219232
VerbResponse priorResponse = null;
220233
Map<String, Object> args = new HashMap<>();
221234

222-
var arguments = response.getArguments();
235+
223236
if (null != response) {
237+
var arguments = response.getArguments();
224238
// Handle memory lookup if specified
225239
if (response.getMemoryLookup() != null && !response.getMemoryLookup().isEmpty()) {
226240
log.info("Memory lookup requested: {}", response.getMemoryLookup());
@@ -263,7 +277,10 @@ public void onApplicationEvent(final ApplicationReadyEvent event) {
263277
}
264278
}
265279

266-
if (response.getNextOperation() != null && !response.getNextOperation().isEmpty()) {
280+
// Check if the response requires execution or is purely conversational
281+
if (response.requiresExecution()) {
282+
currentPlanStatus = "in_progress";
283+
267284
var executionResponse = verbRegistry.execute(
268285
agentExecution,
269286
agentExecutionContext,
@@ -272,6 +289,11 @@ public void onApplicationEvent(final ApplicationReadyEvent event) {
272289
);
273290
verbResponses.add(executionResponse);
274291
lastVerbResponse = executionResponse;
292+
293+
// Track the executed operation
294+
if (!executedOperations.contains(response.getNextOperation())) {
295+
executedOperations.add(response.getNextOperation());
296+
}
275297

276298
var responses = agentExecutionContext.getAgentDataList();
277299
var planResponse =
@@ -285,12 +307,32 @@ public void onApplicationEvent(final ApplicationReadyEvent event) {
285307
}
286308
}
287309
log.info("Plan response: {} from {}", planResponse, responses);
310+
311+
// Use the enhanced interpret_plan_response with executed operations tracking
288312
nextResponse = chatVerbs.interpret_plan_response(
289313
agentExecution,
290314
agentExecutionContext,
291315
verbRegistry.getVerbs().get(response.getNextOperation()),
292-
planResponse
316+
planResponse,
317+
executedOperations,
318+
currentPlanStatus,
319+
isAutonomous
293320
);
321+
322+
// Update plan status from response
323+
if (nextResponse.getPlanStatus() != null) {
324+
currentPlanStatus = nextResponse.getPlanStatus();
325+
}
326+
327+
// Merge executed operations from response
328+
if (nextResponse.getExecutedOperations() != null) {
329+
for (String op : nextResponse.getExecutedOperations()) {
330+
if (!executedOperations.contains(op)) {
331+
executedOperations.add(op);
332+
}
333+
}
334+
}
335+
294336
agentExecutionContext.addToPersistentMemory(
295337
"agent_response_" + System.currentTimeMillis(),
296338
nextResponse.getResponseForUser(),
@@ -337,10 +379,33 @@ public void onApplicationEvent(final ApplicationReadyEvent event) {
337379

338380

339381
response = nextResponse;
382+
// Reset consecutive conversational counter on successful execution
383+
consecutiveConversationalResponses = 0;
384+
} else {
385+
// Response is conversational only - no execution needed
386+
consecutiveConversationalResponses++;
387+
log.info("Response is conversational only (count: {}), no execution needed. Plan status: {}",
388+
consecutiveConversationalResponses, response.getPlanStatus());
389+
currentPlanStatus = response.getPlanStatus() != null ? response.getPlanStatus() : "idle";
390+
391+
// Check if we've exceeded the maximum consecutive conversational responses
392+
if (consecutiveConversationalResponses >= MAX_CONSECUTIVE_CONVERSATIONAL) {
393+
log.info("Maximum consecutive conversational responses ({}) reached. Agent will wait for new user input.",
394+
MAX_CONSECUTIVE_CONVERSATIONAL);
395+
// Reset counter and set response to null to trigger fresh prompt on next iteration
396+
consecutiveConversationalResponses = 0;
397+
response = null;
398+
} else {
399+
// Re-prompt for next action with current plan state
400+
response = chatVerbs.promptAgent(agentExecution, agentExecutionContext, prompt,
401+
executedOperations, currentPlanStatus, isAutonomous);
402+
}
340403
}
341404

342405
}else {
343-
response = chatVerbs.promptAgent(agentExecution, agentExecutionContext, prompt);
406+
response = chatVerbs.promptAgent(agentExecution, agentExecutionContext, prompt,
407+
executedOperations, currentPlanStatus, isAutonomous);
408+
consecutiveConversationalResponses = 0;
344409

345410
}
346411

0 commit comments

Comments
 (0)