fix(teamsbot): filter bot's own captions from AI trigger, mark bot responses in context to prevent repetition

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
patrick-motsch 2026-02-15 22:52:02 +01:00
parent 33b6da0d18
commit cc86b144ac

View file

@ -307,6 +307,11 @@ class TeamsbotService:
if not text:
return
# Filter out the bot's own speech from AI triggering.
# The bot hears itself via captions — these should be stored in the
# transcript for the record, but must NOT trigger AI analysis (feedback loop).
isBotSpeaker = self._isBotSpeaker(speaker)
# Store transcript segment
transcriptData = TeamsbotTranscript(
sessionId=sessionId,
@ -346,6 +351,11 @@ class TeamsbotService:
count = session.get("transcriptSegmentCount", 0) + 1
interface.updateSession(sessionId, {"transcriptSegmentCount": count})
# Skip AI analysis for bot's own speech (prevents feedback loop)
if isBotSpeaker:
logger.debug(f"Session {sessionId}: Skipping AI trigger for bot's own speech: [{speaker}] {text[:60]}...")
return
# Check if AI analysis should be triggered (only for final transcripts)
if not isFinal:
return
@ -364,6 +374,32 @@ class TeamsbotService:
logger.info(f"Session {sessionId}: Triggering AI analysis (buffer: {len(self._contextBuffer)} segments)")
await self._analyzeAndRespond(sessionId, interface, voiceInterface, websocket, createdTranscript)
def _isBotSpeaker(self, speaker: str) -> bool:
"""Check if a transcript speaker is the bot itself.
Teams captions show the bot as e.g. "Shelly Miller (Unverified)" or
"Nyla Larsson" depending on auth/anonymous join. We match against:
- The configured bot name (e.g. "Shelly Miller")
- The bot account display name if authenticated
"""
if not speaker:
return False
speakerLower = speaker.lower().strip()
# Match against configured bot name
botName = self.config.botName.lower().strip()
if botName and botName in speakerLower:
return True
# Match against bot account email prefix (e.g. "nyla.larsson" from "nyla.larsson@poweron.swiss")
if self.config.botAccountEmail:
emailPrefix = self.config.botAccountEmail.split("@")[0].lower().replace(".", " ")
if emailPrefix in speakerLower:
return True
return False
def _shouldTriggerAnalysis(self, transcriptText: str) -> bool:
"""
Decide whether to trigger AI analysis based on the latest transcript.
@ -405,12 +441,16 @@ class TeamsbotService:
"""Run SPEECH_TEAMS AI analysis and respond if needed."""
self._lastAiCallTime = time.time()
# Build transcript context from buffer
# Build transcript context from buffer.
# Mark bot's own utterances so the AI knows what it already said.
contextLines = []
for segment in self._contextBuffer:
speaker = segment.get("speaker", "Unknown")
text = segment.get("text", "")
contextLines.append(f"[{speaker}]: {text}")
if self._isBotSpeaker(speaker):
contextLines.append(f"[YOU ({self.config.botName})]: {text}")
else:
contextLines.append(f"[{speaker}]: {text}")
transcriptContext = f"BOT_NAME:{self.config.botName}\n" + "\n".join(contextLines)