📝 Originally published (in Japanese) at forge.workstyle.tech.
The Log of Voice Dialogue Avatar Had This Interval
👤 This page's content is explained 🤖 The corresponding description was not found.
👤 Can you hear me? 🤖 (Same apology)
👤 Can you hear me again? 🤖 (Same apology)
👤 I can hear you. 🤖 (Same apology)
4 consecutive turns returned almost the same apology. The more the user tries to confirm, the more the same answer is returned.
The Cause Was the Improvement I Made Earlier
A little while ago, I made the following improvement: "Detailed" phrases like "Tell me more about that" cannot be searched alone, so add the previous conversation (user's speech + avatar's answer) to the search. The measurement was also effective (0.602 → 0.741).
Looking at the log, it was like this:
[retrieval] Add the previous conversation:
'Can you hear me? I apologize, but the corresponding description was not found.'
The apology sentence without grounds was also included in the search query.
The apology sentence has no topic. Not only that, but words like "corresponding description" and "another way of saying" pull the search in a completely unrelated direction. That's why the next one is also wrong. And the failed sentence is included in the next query.
The failure was reinforcing itself.
Don't Judge by String
At first, I thought of "detecting and excluding apology sentences". I stopped soon.
Apology phrases differ depending on the job profile. They can be set differently for window, support, and delivery, and can change during operation. Judging by string would create holes every time a new phrase is added.
Instead, I decided based on facts.
if not getattr(self, "_last_turn_grounded", False):
a = "" # Don't add answers from turns without grounds
"Whether there was a ground for that turn" is already judged by the system. It doesn't depend on phrases.
⚠️ I shouldn't have written this update in the recording process. Since conversation log recording is best-effort (it doesn't stop the conversation even if it fails), if an exception occurs, the update will also fail. Even if the recording fails, the presence or absence of grounds affects the search quality of the next turn. I moved it to the end of the turn.
⚠️ The apology path also needed to be marked. If I drop it, the apology sentence will be included in the next query.
Another Thing That Went Against the Facts
While investigating the same 4 turns, I noticed another problem. The page text was not held even once during that time.
[page] Use the full text ← This log didn't appear even once during the 4 turns
I was answering "This page does not contain the corresponding description" without holding the page text. This is against the facts. Whether the visitor can't read it or it's not there after reading is completely different for the visitor.
No text or index → "I'm currently reading the page content. Please wait a moment."
Text exists but no match → "The corresponding description was not found on the page I'm currently viewing."
⚠️ I almost made another mistake. The synthesis audio reuse key didn't distinguish between phrases. Since the standard response reuses audio by caching, if left as is, the audio for "reading" would play when intending to say "not found". I split the key by phrase.
Result
👤 What's the weather like today? → 🤖 (Try to answer the page content)
👤 Tell me about the weather. → 🤖 This page does not contain weather information, so I apologize.
👤 (Next question) → 🤖 (Answer normally) ← Apology doesn't chain
Generalizable Things
Designs that return output to input amplify when they fail.
Using conversation history as context is a natural design. However, there are two types of "avatar answers":
Answers with grounds … Contain topics. Useful as context
Apology answers without grounds … Contain no topics. Rather, distort the search
I should have used only the former. The system already had the material to distinguish (whether there was a ground for that turn), but I was treating answers as one thing.
Similar structures exist elsewhere. Using summaries as input for the next summary, mixing generated data into learning data, using search results as the next search query — all of them "get better when good, worse when bad". A mechanism to stop when bad is needed.
And the judgment to stop should be based on facts, not strings. Phrases change, but "whether there was a ground" doesn't change.
Series: Until the Voice Dialogue Avatar Answers Correctly
This article is the last part of Part 2: Understanding Language.
← Previous: The one line at the end of a huge prompt was ignored four times
→ Next: Not all utterances are questions
Series of 8 articles
Part 1: Stopping Sound
- There were two types of events with the same name
- The self-echo countermeasure never fired
- A finger on the speaker was breaking the echo canceller
Part 2: Understanding Language
- The three "its" were different things
- The one line at the end of a huge prompt was ignored four times
- The apology words were poisoning the search ← Now here
Part 3: Judging
- Not all utterances are questions
- I thought I was measuring, but I was measuring something else
The notes that led to this insight are summarized in Improving the Response Quality of Voice Dialogue Avatars.
Top comments (1)
This is a good example of why a retry should not inherit the whole conversational transcript by default. The error explanation is useful to an operator, but it is usually bad search state. I prefer a typed retry envelope: original objective, last tool observation, failure class, changed constraint, and attempt number. Keep apologies and speculative diagnosis out of the next model context unless a policy explicitly asks for them. That also makes repeated failure fingerprints much easier to compare.