Skip to content

Commit 55a9d98

Browse files
committed
fix: better pormpt to detect goal, manages end session from agents and improve logs
1 parent 0d6bb96 commit 55a9d98

File tree

3 files changed

+82
-9
lines changed

3 files changed

+82
-9
lines changed

pkg/test/agent-runner.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,17 @@ func (atr *AgentTestRunner) ExecuteAgentTest(ctx context.Context, agentTest test
108108
return fmt.Errorf("failed to interact with Voiceflow at step %d: %w", currentStep, err)
109109
}
110110

111+
// Check if the Voiceflow agent's session has ended
112+
if ended, reason := atr.HasEndResponse(voiceflowResponse); ended {
113+
atr.addLog(fmt.Sprintf("Voiceflow agent session ended (reason: %s)", reason))
114+
// Still extract and log the last message if available
115+
lastMessage := atr.ExtractMessage(voiceflowResponse)
116+
if lastMessage != "" {
117+
atr.addLog(fmt.Sprintf("Voiceflow agent final message: %s", lastMessage))
118+
}
119+
break
120+
}
121+
111122
// Get the next action from the AI agent
112123
agentResponse, err = atr.getNextAction(voiceflowResponse, agentTest.Goal, currentStep+1, agentTest.MaxSteps)
113124
if err != nil {

pkg/test/common.go

Lines changed: 54 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,8 @@ func (br *BaseRunner) InteractWithVoiceflow(messageType, message, environmentNam
8383
return br.ProcessResponses(responses), nil
8484
}
8585

86-
// ProcessResponses handles multiple responses by concatenating messages
86+
// ProcessResponses handles multiple responses by concatenating messages.
87+
// It preserves "end" type responses so callers can detect session termination.
8788
func (br *BaseRunner) ProcessResponses(responses []interact.InteractionResponse) []interact.InteractionResponse {
8889
if len(responses) == 0 {
8990
br.AddLog("No response received from Voiceflow")
@@ -93,21 +94,43 @@ func (br *BaseRunner) ProcessResponses(responses []interact.InteractionResponse)
9394
// If there are multiple responses, concatenate their messages
9495
if len(responses) > 1 {
9596
var concatenatedMessage strings.Builder
97+
var endResponse *interact.InteractionResponse
98+
firstMessageIdx := -1
99+
96100
for i, response := range responses {
101+
// Preserve end response for session termination detection
102+
if response.Type == "end" {
103+
endResponse = &responses[i]
104+
continue
105+
}
97106
if message, ok := response.Payload["message"].(string); ok && message != "" {
98-
if i > 0 {
107+
if firstMessageIdx == -1 {
108+
firstMessageIdx = i
109+
} else {
99110
concatenatedMessage.WriteString(" ")
100111
}
101112
concatenatedMessage.WriteString(message)
102113
}
103114
}
104115

105-
// Update the first response with the concatenated message
106-
if concatenatedMessage.Len() > 0 {
107-
responses[0].Payload["message"] = concatenatedMessage.String()
116+
var result []interact.InteractionResponse
117+
118+
// Update the first message response with the concatenated message
119+
if firstMessageIdx >= 0 && concatenatedMessage.Len() > 0 {
120+
responses[firstMessageIdx].Payload["message"] = concatenatedMessage.String()
121+
result = append(result, responses[firstMessageIdx])
122+
}
123+
124+
// Append end response if present so callers can detect session end
125+
if endResponse != nil {
126+
result = append(result, *endResponse)
127+
}
128+
129+
if len(result) > 0 {
130+
return result
108131
}
109132

110-
// Return only the first response with the concatenated message
133+
// Fallback: return the original first response
111134
return responses[:1]
112135
}
113136

@@ -124,6 +147,23 @@ func (br *BaseRunner) ExtractMessage(voiceflowResponse []interact.InteractionRes
124147
return ""
125148
}
126149

150+
// HasEndResponse checks if any of the Voiceflow responses contain an "end" type trace,
151+
// which indicates the agent's session has ended.
152+
func (br *BaseRunner) HasEndResponse(responses []interact.InteractionResponse) (bool, string) {
153+
for _, response := range responses {
154+
if response.Type == "end" {
155+
reason := ""
156+
if response.Payload != nil {
157+
if r, ok := response.Payload["reason"].(string); ok {
158+
reason = r
159+
}
160+
}
161+
return true, reason
162+
}
163+
}
164+
return false, ""
165+
}
166+
127167
// IsGoalAchieved uses OpenAI to evaluate if the goal has been achieved
128168
func (br *BaseRunner) IsGoalAchieved(goal string) (bool, error) {
129169
// Build conversation summary
@@ -141,17 +181,22 @@ func (br *BaseRunner) IsGoalAchieved(goal string) (bool, error) {
141181
}
142182
}
143183

144-
prompt := fmt.Sprintf(`Analyze the following conversation and determine if the goal has been achieved.
184+
prompt := fmt.Sprintf(`Analyze the following conversation between two agents and determine if the stated goal has been achieved or is clearly being fulfilled.
145185
146186
Goal: %s
147187
148188
Conversation:
149189
%s
150190
151-
Has the goal been achieved? Respond with only "YES" or "NO".`, goal, conversationSummary.String())
191+
When evaluating, consider:
192+
- Has the goal been explicitly completed (e.g., a clear confirmation message)?
193+
- Has the goal been effectively achieved even without an explicit final confirmation? For example, if all required information has been gathered and the action is being processed, or both parties are acting as if the goal is accomplished, consider it achieved.
194+
- Look at the overall intent and progression of the conversation, not just the last message.
195+
196+
Based on the full conversation context, has the goal been achieved or effectively fulfilled? Respond with only "YES" or "NO".`, goal, conversationSummary.String())
152197

153198
messages := []ChatMessage{
154-
{Role: "system", Content: "You are a helpful assistant that analyzes conversations and determines if goals have been achieved."},
199+
{Role: "system", Content: "You are an expert evaluator that analyzes conversations between two agents and determines if a stated goal has been achieved or effectively fulfilled. Consider the overall intent, actions taken, and progression of the conversation. A goal can be considered achieved if the necessary actions have been completed or are clearly being executed, even without an explicit final confirmation message."},
155200
{Role: "user", Content: prompt},
156201
}
157202

pkg/test/voiceflow-agent-runner.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,12 @@ func (vatr *VoiceflowAgentTestRunner) ExecuteAgentTest(ctx context.Context, agen
128128

129129
vatr.addLog(fmt.Sprintf("Step %d", currentStep))
130130

131+
// Check if the tester agent's session has ended
132+
if ended, reason := vatr.HasEndResponse(testerResponse); ended {
133+
vatr.addLog(fmt.Sprintf("Tester agent session ended (reason: %s)", reason))
134+
break
135+
}
136+
131137
// Get the tester's message and send it to the target agent
132138
testerMessage := vatr.ExtractMessage(testerResponse)
133139
if testerMessage == "" {
@@ -146,6 +152,17 @@ func (vatr *VoiceflowAgentTestRunner) ExecuteAgentTest(ctx context.Context, agen
146152
return fmt.Errorf("failed to interact with target agent at step %d: %w", currentStep, err)
147153
}
148154

155+
// Check if the target agent's session has ended
156+
if ended, reason := vatr.HasEndResponse(targetAgentResponse); ended {
157+
vatr.addLog(fmt.Sprintf("Target agent session ended (reason: %s)", reason))
158+
// Still extract and log the last message if available
159+
targetMessage := vatr.ExtractMessage(targetAgentResponse)
160+
if targetMessage != "" {
161+
vatr.addLog(fmt.Sprintf("Target agent final message: %s", targetMessage))
162+
}
163+
break
164+
}
165+
149166
targetMessage := vatr.ExtractMessage(targetAgentResponse)
150167
vatr.addLog(fmt.Sprintf("Target agent says: %s", targetMessage))
151168

0 commit comments

Comments
 (0)