From 794722673bfc7b554bc8030d453eb705629e7076 Mon Sep 17 00:00:00 2001 From: David Kaya Date: Mon, 23 Mar 2026 23:22:09 +0100 Subject: [PATCH] fix: strengthen group chat collaboration guidance Audit confirmed that single, sequential, concurrent, and handoff are mapped to distinct workflow builders as intended. Group-chat was also using the correct round-robin manager, but the agents only had one-shot role prompts, so the reviewer could behave like a fresh assistant instead of refining the draft in progress. Add explicit runtime guidance for group-chat participants plus clearer built-in Writer/Reviewer instructions and regression coverage. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../Services/AgentInstructionComposer.cs | 19 +++++++++++++ .../AgentInstructionComposerTests.cs | 28 +++++++++++++++++++ src/shared/domain/pattern.ts | 6 ++-- tests/shared/pattern.test.ts | 11 ++++++++ 4 files changed, 62 insertions(+), 2 deletions(-) diff --git a/sidecar/src/Eryx.AgentHost/Services/AgentInstructionComposer.cs b/sidecar/src/Eryx.AgentHost/Services/AgentInstructionComposer.cs index 729a5cf..f47a1a9 100644 --- a/sidecar/src/Eryx.AgentHost/Services/AgentInstructionComposer.cs +++ b/sidecar/src/Eryx.AgentHost/Services/AgentInstructionComposer.cs @@ -20,6 +20,25 @@ internal static class AgentInstructionComposer """ : string.Empty; + if (string.Equals(pattern.Mode, "group-chat", StringComparison.OrdinalIgnoreCase)) + { + string groupChatGuidance = agentIndex == 0 + ? """ + You are participating in a collaborative multi-turn group chat under a round-robin manager. + On your first turn, produce the initial draft for the user. + On later turns, refine your earlier draft based on the other agents' feedback instead of restarting from scratch. + Do not greet the user again or reset the conversation once work is underway. + """ + : """ + You are participating in a collaborative multi-turn group chat under a round-robin manager. + Build on the latest draft from the other agents and contribute specific critique or improvements. + Do not restart the conversation, greet the user again, or answer as though no draft exists yet. + Focus on refining the answer already in progress. + """; + + return JoinInstructionBlocks(baseInstructions, workspaceGuidance, groupChatGuidance); + } + if (!string.Equals(pattern.Mode, "handoff", StringComparison.OrdinalIgnoreCase)) { return JoinInstructionBlocks(baseInstructions, workspaceGuidance); diff --git a/sidecar/tests/Eryx.AgentHost.Tests/AgentInstructionComposerTests.cs b/sidecar/tests/Eryx.AgentHost.Tests/AgentInstructionComposerTests.cs index 343d38d..fdac684 100644 --- a/sidecar/tests/Eryx.AgentHost.Tests/AgentInstructionComposerTests.cs +++ b/sidecar/tests/Eryx.AgentHost.Tests/AgentInstructionComposerTests.cs @@ -25,6 +25,34 @@ public sealed class AgentInstructionComposerTests Assert.Equal("Review the proposal.", instructions); } + [Fact] + public void Compose_StrengthensGroupChatCollaborationRoles() + { + PatternDefinitionDto pattern = new() + { + Id = "pattern-group-chat", + Name = "Group Chat", + Mode = "group-chat", + Availability = "available", + }; + PatternAgentDefinitionDto writer = CreateAgent( + id: "agent-group-writer", + name: "Writer", + instructions: "Draft an answer."); + PatternAgentDefinitionDto reviewer = CreateAgent( + id: "agent-group-reviewer", + name: "Reviewer", + instructions: "Review the draft."); + + string writerInstructions = AgentInstructionComposer.Compose(pattern, writer, agentIndex: 0); + string reviewerInstructions = AgentInstructionComposer.Compose(pattern, reviewer, agentIndex: 1); + + Assert.Contains("collaborative multi-turn group chat", writerInstructions, StringComparison.OrdinalIgnoreCase); + Assert.Contains("refine your earlier draft", writerInstructions, StringComparison.OrdinalIgnoreCase); + Assert.Contains("specific critique or improvements", reviewerInstructions, StringComparison.OrdinalIgnoreCase); + Assert.Contains("Do not restart the conversation", reviewerInstructions, StringComparison.OrdinalIgnoreCase); + } + [Fact] public void Compose_StrengthensHandoffTriageInstructions() { diff --git a/src/shared/domain/pattern.ts b/src/shared/domain/pattern.ts index 64a8ff5..8da7239 100644 --- a/src/shared/domain/pattern.ts +++ b/src/shared/domain/pattern.ts @@ -204,7 +204,8 @@ export function createBuiltinPatterns(timestamp: string): PatternDefinition[] { id: 'agent-group-writer', name: 'Writer', description: 'Produces candidate answers.', - instructions: 'You draft a concise, useful answer for the task.', + instructions: + 'You draft a concise, useful answer for the task. On later turns, refine your earlier draft based on peer feedback rather than restarting.', model: defaultModels.gpt54, reasoningEffort: 'medium', }, @@ -212,7 +213,8 @@ export function createBuiltinPatterns(timestamp: string): PatternDefinition[] { id: 'agent-group-reviewer', name: 'Reviewer', description: 'Critiques and refines the answer.', - instructions: 'You review the latest answer and improve it when needed.', + instructions: + 'You review the latest draft and offer specific improvements. Focus on critique and refinement instead of restarting the conversation.', model: defaultModels.claude, reasoningEffort: 'medium', }, diff --git a/tests/shared/pattern.test.ts b/tests/shared/pattern.test.ts index df65101..26d448d 100644 --- a/tests/shared/pattern.test.ts +++ b/tests/shared/pattern.test.ts @@ -90,4 +90,15 @@ describe('pattern validation', () => { expect(handoff?.agents[1].instructions).toContain('own the substantive answer'); expect(handoff?.agents[2].instructions).toContain('own the substantive answer'); }); + + test('group chat builtin instructions frame iterative drafting and review', () => { + const groupChat = createBuiltinPatterns(BUILTIN_TIMESTAMP).find( + (pattern) => pattern.mode === 'group-chat', + ); + + expect(groupChat).toBeDefined(); + expect(groupChat?.agents[0].instructions).toContain('refine your earlier draft'); + expect(groupChat?.agents[1].instructions).toContain('specific improvements'); + expect(groupChat?.agents[1].instructions).toContain('instead of restarting the conversation'); + }); });