跳到正文
DEV Community· StareBrain·· 3 小時前AI 評分45

構建 Android AI Agent(第 14 部分):當智能體對用戶的判斷有誤

Building an Android AI Agent, Part 14: When the Agent’s Model of You Is Wrong

AI 導讀

這篇 Android AI Agent 系列文章探討智能體對用戶意圖判斷有誤時,如何識別情境不符並作出補救。智能體可透過首個步驟立即失敗,以及 HIGH、MEDIUM 或 LOW 信心等級判斷是否需要重新規劃或詢問用戶。新指令若與先前意圖不符,系統會清除舊意圖,避免沿用過時情境。

正文

Building an Android AI Agent, Part 14: When the Agent's Model of You Is Wrong

In Part 13 we wired AgentContext into the full pipeline. The agent now observes app switches, builds a confidence-weighted model of what the user is doing, and uses that model to generate a step list without asking clarifying questions.

That works well when the model is right. This part covers what happens when it's wrong.

Three ways AgentContext misfires

1. Misread app switch pattern

Three app switches in 60 seconds signals HIGH confidence. But those switches might not be task-related — the user could have been responding to a string of notifications, each pulling them into a different app. The pattern looks like active work; it was actually reactive context-jumping.

The agent infers: "user is drafting something in Gmail."
The user wants: "set a timer for 10 minutes."

2. Stale intent carried forward

lastUserIntent persists across sessions in short-term memory. If the user's last session ended mid-task — say, they were composing an email — that intent surfaces in the next session's context even if the user has moved on entirely.

The agent infers: "user wants to continue the email draft."
The user wants: "open Spotify."

3. Package name mismatch

targetPackage in a step is inferred from context. If two apps share similar behavior (two different email clients, for example), the agent might target the wrong one.

The agent tries to: open Gmail.
The user's default email app: Outlook.

Detecting a mismatch

The cheapest signal is a first-step failure. If step one fails immediately — the target element isn't on screen, the package didn't open, the action returned nothing — that's a strong indicator the context model was wrong, not that the app is broken.

fun isContextMismatch(
    step: AgentStep,
    result: StepResult,
    context: AgentContext
): Boolean {
    if (result.status != StepStatus.FAILED) return false
    if (step != context.inferredFirstStep) return false // only check context-derived steps
    // First step failed immediately — likely a bad context inference
    return context.confidenceLevel != ContextConfidence.LOW
}

A LOW confidence context that fails isn't surprising — we already knew the model was weak. A HIGH or MEDIUM confidence context that fails on step one is the mismatch signal.

Recovering without losing the user

When a mismatch is detected, the agent has two options: replan with a corrected context, or ask.

For HIGH confidence mismatches, we ask — the agent was confident and wrong, which means the correction needs to come from the user, not from another inference:

suspend fun handleContextMismatch(
    task: AgentTask,
    context: AgentContext
): MismatchResolution {
    return when (context.confidenceLevel) {
        ContextConfidence.HIGH -> {
            // Was confident, was wrong — ask explicitly
            MismatchResolution.AskUser(
                message = "I expected to find ${context.activePackage} but the screen looks different. What would you like to do?"
            )
        }
        ContextConfidence.MEDIUM -> {
            // Had some signal — try replanning with reduced confidence
            val degraded = context.copy(confidenceLevel = ContextConfidence.LOW)
            MismatchResolution.Replan(degraded)
        }
        ContextConfidence.LOW -> {
            // Already uncertain — ask
            MismatchResolution.AskUser(
                message = "What would you like to do?"
            )
        }
    }
}

sealed class MismatchResolution {
    data class AskUser(val message: String) : MismatchResolution()
    data class Replan(val degradedContext: AgentContext) : MismatchResolution()
}

MEDIUM confidence gets one replan attempt with the context degraded to LOW — essentially telling the LLM "our model of the situation was uncertain, here's what we actually see on screen now." If that also fails, it surfaces to the user.

Correcting lastUserIntent

Stale intent is handled at the context-build stage, not at recovery. When the user gives an instruction that clearly doesn't match the last intent, the new instruction replaces it immediately:

fun AgentContextBuilder.updateIntent(newInstruction: String) {
    // If the new instruction doesn't reference the previous task at all,
    // clear the stale intent rather than carrying it forward
    if (lastUserIntent != null && !newInstruction.contains(lastUserIntent!!, ignoreCase = true)) {
        lastUserIntent = null
    }
    lastUserIntent = newInstruction
}

This is a simple string check — if the new instruction shares no language with the previous intent, the old intent is cleared. It's not perfect, but it prevents the agent from confidently doing the wrong thing based on a session that ended hours ago.

What the user sees

Mismatch recovery should feel like a natural correction, not a failure. The phrasing matters:

  • "I expected X but the screen looks different. What would you like to do?" — honest, non-technical, invites the user to redirect
  • "Something went wrong, please try again." — opaque, unhelpful, makes the agent feel broken

The agent's confidence in its own model should be visible to the user in proportion to how

來源:DEV Community · dev.to