Files
CamTalk/docs/fix-text-input-camera-issue.md
cfy666 ed95ce56a8 fix: 关闭摄像头后文本输入不再发送图像数据
修复了用户关闭摄像头后,使用文本输入时 AI 仍会分析黑色画面的问题。

变更:
- sendTextMessage: 只在摄像头开启时捕获画面
- 待发消息队列: 根据摄像头状态决定是否携带画面

效果:
- 摄像头关闭时纯文本对话,不提及画面
- 节省 token 消耗和网络带宽

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 22:21:03 +08:00

136 lines
3.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 修复文本输入时仍调用摄像头的问题
## 问题描述
当用户关闭视频对话改用文本输入聊天时AI 仍然会说"画面是黑色的",然后才回答问题。这是因为即使摄像头关闭,前端仍然发送了图像数据(黑屏)给后端。
## 问题根源
### 前端问题
`frontend/src/hooks/useVisionSession.ts` 中:
1. **sendTextMessage 函数**(第 441 行):无论摄像头是否开启,都会调用 `captureFrame()` 捕获画面
2. **待发消息队列处理**(第 140 行):连接成功后 flush 待发消息时,也会无条件捕获画面
```typescript
// 问题代码
const frame = captureFrame(); // 即使摄像头关闭也会捕获(返回黑屏)
send({
type: "query",
image: frame ? dataUrlToBase64(frame) : "", // 发送黑屏数据
text: text.trim(),
});
```
### 后端行为
`backend/internal/eino/nodes_history.go` 中:
- 第 68-86 行:只要收到图像数据(`len(imageData) > 0`),就会构建多模态消息发送给 LLM
- LLM 会分析这个黑色画面,导致回复中提到"画面是黑色的"
## 解决方案
### 修改内容
修改了两处代码,都在 `frontend/src/hooks/useVisionSession.ts`
#### 1. sendTextMessage 函数(第 421-462 行)
```typescript
// 修改前
const frame = captureFrame();
// 修改后
// 只在摄像头开启时才捕获画面,避免发送黑屏给 AI
const frame = isCameraOn ? captureFrame() : null;
```
并在依赖数组中添加了 `isCameraOn`
```typescript
[captureFrame, send, connect, accessToken, isCameraOn]
```
#### 2. 待发消息队列处理(第 124-156 行)
```typescript
// 修改前
const frame = captureFrame();
// 修改后
// 只在摄像头开启时才捕获画面,避免发送黑屏给 AI
const frame = isCameraOn ? captureFrame() : null;
```
### 效果
修改后:
- ✅ 摄像头**开启**时,文本输入仍然会携带当前画面(多模态对话)
- ✅ 摄像头**关闭**时,文本输入只发送文字(纯文本对话)
- ✅ AI 不会再说"画面是黑色的"
- ✅ 节省 token 消耗(不发送无用的图像数据)
## 测试场景
1. **开启视频对话 → 关闭摄像头 → 文本输入聊天**
- 预期AI 只回答文本问题,不提及画面
2. **直接文本输入(未开启摄像头)**
- 预期:纯文本对话,不发送图像数据
3. **开启摄像头 → 文本输入**
- 预期AI 可以看到画面并结合文本回答
4. **未连接时文本输入 → 自动连接**
- 预期:待发消息在连接成功后发送,根据当时的摄像头状态决定是否携带画面
## 替代方案(未采用)
### 方案二:后端过滤空图像
在后端 `nodes_history.go` 中增加图像有效性检测:
```go
// 检测图像是否有效(非全黑/全白)
func isValidImage(data []byte) bool {
// 检测文件大小(黑屏图像通常很小)
if len(data) < 1024 { // 小于 1KB
return false
}
// 可以进一步检测像素方差等
return true
}
// 在 NewHistoryLambda 中
if len(imageData) > 0 && isValidImage(imageData) {
// 构建多模态消息
} else {
// 纯文本消息
}
```
**未采用原因**
- 前端方案更直接,从源头解决问题
- 避免后端做额外的图像检测(性能开销)
- 节省网络传输(不发送无用的图像数据)
## 相关文件
- `frontend/src/hooks/useVisionSession.ts` - 核心会话逻辑
- `backend/internal/eino/nodes_history.go` - 后端历史消息组装
- `docs/02-接口文档.md` - WebSocket 协议定义
## 提交信息
```
fix: 关闭摄像头后文本输入不再发送图像数据
修复了用户关闭摄像头后,使用文本输入时 AI 仍会分析黑色画面的问题。
变更:
- sendTextMessage: 只在摄像头开启时捕获画面
- 待发消息队列: 根据摄像头状态决定是否携带画面
效果:
- 摄像头关闭时纯文本对话,不提及画面
- 节省 token 消耗和网络带宽
```