第2404篇:AI功能的可访问性设计——让AI对更多用户群体友好
第2404篇:AI功能的可访问性设计——让AI对更多用户群体友好
适读人群:负责AI产品开发的工程师和产品团队 | 阅读时长:约11分钟 | 核心价值:理解AI可访问性的核心维度,避免AI功能成为某些用户群体的障碍
做过一次特别有收获的用户调研。
当时我们的AI问答功能已经上线了两个月,各项数据都还不错。但有个产品经理发现了一个奇怪的现象:使用率按年龄段分布极度不均匀,40岁以上的用户使用率只有同龄用户整体功能使用率的18%。
我们做了一些访谈,结论让人惭愧:
一个47岁的用户说:「我不知道这个AI对话框在哪,找了半天没找到。」(我们把入口做成了一个很小的图标,年轻用户用习惯了,老年用户根本没注意到。)
另一个用户说:「AI说的话太长了,我看不完,不知道结论是什么。」(我们没有做摘要或结论高亮。)
还有一个用户说:「我打字慢,等AI生成这段时间我已经想不起来我问的什么了。」(对于打字慢的用户,输入和输出之间的时间差造成了认知断层。)
这些问题不是AI本身的技术问题,而是可访问性问题——我们的AI功能对特定用户群体不友好。
AI可访问性的四个维度
可访问性(Accessibility)通常被理解为「残障人士能不能用」,但在AI产品中,可访问性的内涵要宽得多:
感知可访问性:包括视觉障碍(色觉异常、视力弱)、屏幕阅读器支持、AI处理状态的多感官反馈。
认知可访问性:包括AI输出的复杂度、阅读障碍用户、认知负荷过高的交互流程。
操作可访问性:包括运动障碍(只能用键盘)、打字慢(需要语音输入)、触摸精度差(手抖的老年用户)。
语言可访问性:包括非母语用户、AI对方言/口语的理解能力、输出文字的可读性。
工程师能做的可访问性改进
1. 让AI输出适配不同用户的认知水平
同一个问题的回答,对专业用户和普通用户应该是不同的。
@Service
public class AdaptiveResponseService {
private final ChatClient chatClient;
private final UserProfileService userProfileService;
/**
* 根据用户画像动态调整AI回答的复杂度
*/
public String generateAdaptiveResponse(String userId, String question) {
UserProfile profile = userProfileService.getProfile(userId);
String systemPrompt = buildAdaptiveSystemPrompt(profile);
return chatClient.prompt()
.system(systemPrompt)
.user(question)
.call()
.content();
}
private String buildAdaptiveSystemPrompt(UserProfile profile) {
return switch (profile.getExpertiseLevel()) {
case BEGINNER -> """
用户是该领域的新手。请用以下方式回答:
- 避免专业术语,如果必须使用,请立即解释
- 用类比和例子来说明概念
- 回答控制在150字以内,突出最重要的一个结论
- 在结尾推荐下一步可以了解的内容
""";
case INTERMEDIATE -> """
用户有一定的基础知识。请用以下方式回答:
- 可以使用专业术语,但对冷僻词汇加以说明
- 回答结构清晰,结论在前,细节在后
- 回答控制在300字以内
""";
case EXPERT -> """
用户是该领域的专业人员。请直接提供专业的技术细节,
不需要解释基础概念,可以使用专业术语,注重准确性和完整性。
""";
};
}
/**
* 为认知负担较重的用户提供摘要版本
*/
public ResponseWithSummary generateWithSummary(String question, String fullResponse) {
String summaryPrompt = """
请为以下回答生成一个不超过30字的核心结论摘要,
用一句话告诉读者最重要的信息是什么:
%s
""".formatted(fullResponse);
String summary = chatClient.prompt()
.user(summaryPrompt)
.call()
.content();
return new ResponseWithSummary(summary, fullResponse);
}
record ResponseWithSummary(String keySummary, String fullResponse) {}
}2. 多语言和方言支持
中国用户的语言使用情况比很多工程师想象的复杂得多——粤语用户、少数民族用户、外籍用户都有可能是你的用户群体。
@Service
public class MultilingualAIService {
private final ChatClient chatClient;
private final LanguageDetector languageDetector;
/**
* 检测用户输入语言,用相同语言回答
*/
public String respondInUserLanguage(String userId, String userInput) {
DetectedLanguage inputLanguage = languageDetector.detect(userInput);
String systemPrompt = """
你是一个多语言AI助手。
请务必用以下语言回答用户:%s
如果无法确定用户使用的具体语言,请用简体中文回答。
""".formatted(inputLanguage.getFullName());
return chatClient.prompt()
.system(systemPrompt)
.user(userInput)
.call()
.content();
}
/**
* 处理粤语输入(LLM通常对粤语理解能力较弱)
* 先把粤语翻译成普通话,再处理
*/
public String handleCantoneseInput(String cantoneseInput) {
String translationPrompt = """
请将以下粤语文本翻译成标准普通话,保持原意不变:
%s
只返回翻译结果,不需要解释。
""".formatted(cantoneseInput);
String mandarinText = chatClient.prompt()
.user(translationPrompt)
.call()
.content();
// 然后用普通话处理问题,但用粤语回答
String answerPrompt = """
用户用粤语问了以下问题(已翻译):%s
请用粤语回答这个问题。
""".formatted(mandarinText);
return chatClient.prompt()
.user(answerPrompt)
.call()
.content();
}
}3. 语音输入支持(运动障碍 / 打字困难用户)
对于打字慢或有运动障碍的用户,语音输入是关键的可访问性功能:
@RestController
@RequestMapping("/api/v1/ai/voice")
public class VoiceInputController {
private final SpeechToTextService sttService;
private final AIService aiService;
/**
* 接受语音输入,转换为文字后处理
*/
@PostMapping("/query")
public ResponseEntity<VoiceQueryResponse> handleVoiceQuery(
@RequestParam("audio") MultipartFile audioFile,
@RequestParam String userId,
@RequestParam(required = false, defaultValue = "zh-CN") String language) {
// 语音转文字
SpeechToTextResult sttResult = sttService.transcribe(
audioFile.getBytes(), language);
if (sttResult.confidence() < 0.6) {
return ResponseEntity.ok(VoiceQueryResponse.lowConfidence(
sttResult.text(),
"语音识别可能不准确,请确认以下内容是否是您的问题:" + sttResult.text()
));
}
// 处理识别出的文字
String aiResponse = aiService.process(userId, sttResult.text());
return ResponseEntity.ok(VoiceQueryResponse.success(
sttResult.text(), // 把识别出的文字展示给用户,让他们确认
aiResponse
));
}
record VoiceQueryResponse(
boolean success,
String recognizedText,
String aiResponse,
String warningMessage
) {
static VoiceQueryResponse success(String text, String response) {
return new VoiceQueryResponse(true, text, response, null);
}
static VoiceQueryResponse lowConfidence(String text, String warning) {
return new VoiceQueryResponse(false, text, null, warning);
}
}
}4. 为屏幕阅读器优化AI输出格式
视觉障碍用户使用屏幕阅读器,Markdown格式对他们不友好(会读出 **加粗** 这样的原始标记):
@Service
public class OutputFormatterService {
/**
* 根据客户端类型格式化输出
*/
public String formatForClient(String rawResponse, ClientType clientType) {
return switch (clientType) {
case WEB_BROWSER -> convertMarkdownToHtml(rawResponse);
case SCREEN_READER -> stripFormattingForScreenReader(rawResponse);
case MOBILE_APP -> convertToMobileFormat(rawResponse);
case API -> rawResponse; // API调用者自己处理格式
};
}
/**
* 为屏幕阅读器移除Markdown标记,保留语义结构
*/
private String stripFormattingForScreenReader(String markdown) {
return markdown
// 移除加粗/斜体标记,但保留内容
.replaceAll("\\*\\*(.+?)\\*\\*", "$1")
.replaceAll("\\*(.+?)\\*", "$1")
// 把列表符号转为自然语言
.replaceAll("^- ", "• ")
.replaceAll("^\\d+\\. ", "")
// 把标题转为强调语气
.replaceAll("^#{1,3} (.+)", "【$1】")
// 移除代码块标记
.replaceAll("```[\\s\\S]*?```", "[代码示例,请查看原始文档]")
.trim();
}
/**
* 添加ARIA标签辅助信息(用于前端渲染提示)
*/
public String addAriaMetadata(String content, ContentType contentType) {
return switch (contentType) {
case AI_GENERATED ->
String.format("<div role='region' aria-label='AI生成的内容'>%s</div>",
content);
case AI_PROCESSING ->
"<div role='status' aria-live='polite' aria-label='AI正在处理中'>处理中...</div>";
case AI_ERROR ->
String.format("<div role='alert' aria-label='AI处理出现问题'>%s</div>",
content);
};
}
}5. 超时和等待的可访问性处理
对于认知障碍或注意力短暂的用户,长时间等待会导致他们迷失:
@Service
public class AccessibleWaitingExperienceService {
/**
* 为长时间等待的AI任务提供进度更新
* 防止用户不知道发生了什么而放弃
*/
public Flux<ProgressUpdate> processWithProgressUpdates(String taskId, String userQuery) {
return Flux.create(sink -> {
// 立即发送处理开始的反馈
sink.next(new ProgressUpdate(0, "已收到您的问题,正在分析中..."));
// 2秒后发送中间状态
Mono.delay(Duration.ofSeconds(2))
.subscribe(t -> sink.next(
new ProgressUpdate(30, "正在查找相关信息...")));
// 4秒后发送另一个状态
Mono.delay(Duration.ofSeconds(4))
.subscribe(t -> sink.next(
new ProgressUpdate(60, "正在整理答案,即将完成...")));
// 执行实际的AI任务
try {
String result = aiService.process(userQuery);
sink.next(new ProgressUpdate(100, result));
sink.complete();
} catch (Exception e) {
sink.next(new ProgressUpdate(-1, "处理出现问题,请重试"));
sink.complete();
}
});
}
record ProgressUpdate(int percentage, String message) {}
}可访问性测试清单
在AI功能上线前,用这个清单检查可访问性:
【感知可访问性】
□ AI生成内容有明确的视觉区分(不只依赖颜色)
□ 交互状态有多种感知方式(不只是视觉,也有文字描述)
□ 屏幕阅读器可以正确读取AI输出
□ 图片/图表内容有alt文字描述
【认知可访问性】
□ 关键结论在回答开头(倒金字塔结构)
□ 长回答有摘要或结论高亮
□ 专业术语有解释(或根据用户水平自适应)
□ 错误提示是友好的人话,不是技术错误码
【操作可访问性】
□ 所有功能可以只用键盘完成
□ 点击目标足够大(≥44×44px)
□ 支持语音输入(可选)
□ 重试/纠错的步骤足够简单
【语言可访问性】
□ 支持的语言已在文档中说明
□ 对非母语用户有合理的错误提示
□ 非正式表达/口语也能被正确理解总结
AI的可访问性不是「做完了就行」的检查项,而是产品质量的一部分。
当我们的AI功能对40岁以上用户的使用率只有18%时,这不是用户的问题,这是我们的设计问题。
四个维度:感知、认知、操作、语言可访问性。
工程师能做的:自适应输出、多语言支持、语音输入、屏幕阅读器优化、等待体验改进。
一个好的AI产品,应该让尽可能多的用户都能从中获益,而不只是让年轻、教育程度高、无障碍的用户才能用好。
