Files
aue/前端示例.md
2026-06-03 13:16:30 +08:00

306 lines
7.7 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.
toolName: view\_files
status: success
filePath: d:\Users\19211\Desktop\demo\QAModule.vue
我来查看一下 `aiAPI.sendMessage` 的实现,这很可能是问题所在。
toolName: file\_search
status: success
file\_pattern: \*\*/ai.js
toolName: file\_search
status: success
file\_pattern: **/api/**/\*.{js,ts}
toolName: search\_by\_regex
status: success
query: aiAPI
toolName: search\_by\_regex
status: success
query: sendMessage
toolName: search\_by\_regex
status: success
query: import.\*aiAPI
toolName: file\_search
status: success
file\_pattern: **/api/**
toolName: search\_by\_regex
status: success
query: export.\*sendMessage
toolName: view\_folder
status: success
d:\Users\19211\Desktop\demo
toolName: file\_search
status: success
file\_pattern: \*\*/\*.vue
toolName: search\_by\_regex
status: success
query: @/api
toolName: file\_search
status: success
file\_pattern: \*\*/\*.js
toolName: file\_search
status: success
file\_pattern: \*\*/\*.ts
toolName: view\_files
status: success
filePath: d:\Users\19211\Desktop\demo\前端示例.md
我发现问题了。您的 `QAModule.vue` 代码本身有打字机效果的实现逻辑,但问题很可能是 **`aiAPI.sendMessage`** **没有真正实现流式读取**
让我查看一下后端 `FileService``streamFromRagService` 方法,了解它返回的数据格式:
toolName: search\_by\_regex
status: success
query: streamFromRagService
toolName: search\_by\_regex
status: success
query: streamFromRagService
toolName: search\_by\_regex
status: success
query: streamFromRagService
toolName: view\_files
status: success
filePath: d:\Users\19211\Desktop\demo\src\main\java\top\tqx\demo\_1\service\impl\FileServiceImpl.java
我找到问题根源了!您的代码中 **`@/api/ai`** **文件不存在**,所以我无法看到 `aiAPI.sendMessage` 的具体实现。但根据代码分析,问题很可能出在 **`aiAPI.sendMessage`** **没有真正实现流式读取**
## 问题诊断
后端 `streamFromRagService` 是**真正的 SSE 流式输出**第521行 `emitter.send(event)`),问题在于**前端 API 层没有正确解析 SSE 流**。
您需要创建一个 `@/api/ai.js` 文件来实现正确的流式读取。以下是修复后的代码:
```javascript
// src/api/ai.js 或 @/api/ai.js
import request from '@/utils/request'
export default {
sendMessage(params, onStart, onMessage, onSource, onReference, onError, onComplete) {
const controller = new AbortController()
// 1. 先发送请求
fetch('/ai/chat/stream', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + localStorage.getItem('token')
},
body: JSON.stringify(params),
signal: controller.signal
})
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok')
}
const reader = response.body.getReader()
const decoder = new TextDecoder()
let buffer = ''
// 2. 开始读取流
function read() {
reader.read().then(({ done, value }) => {
if (done) {
// 处理缓冲区中剩余的数据
if (buffer.trim()) {
try {
const data = JSON.parse(buffer.trim())
if (data.type === 'content' && onMessage) {
onMessage(data.content)
}
} catch (e) {}
}
if (onComplete) onComplete({})
return
}
// 解码并添加到缓冲区
buffer += decoder.decode(value, { stream: true })
// 处理每一行(处理不完整的数据行)
const lines = buffer.split('\n')
buffer = lines.pop() // 保留最后未完成的一行
for (const line of lines) {
if (line.startsWith('data: ')) {
const dataStr = line.slice(6).trim()
if (!dataStr || dataStr === '[DONE]') continue
try {
const data = JSON.parse(dataStr)
switch (data.type) {
case 'content':
// 关键:每个字符块都立即调用回调!
if (onMessage) onMessage(data.content)
break
case 'source':
case 'sources':
if (onSource) onSource(data)
break
case 'reference':
if (onReference) onReference(data)
break
case 'session_id':
if (onComplete) onComplete({ sessionId: data.session_id })
break
case 'error':
if (onError) onError(data.message)
break
case 'finish':
case 'result':
// 收集最终结果
if (onComplete) onComplete({
answer: data.answer || '',
sources: data.sources || []
})
break
default:
// 其他事件类型也处理
if (data.answer && onMessage) {
// 有些服务把完整答案放在 answer 字段
onMessage(data.answer)
}
}
} catch (e) {
// JSON 解析失败,忽略该行
}
}
}
// 继续读取
read()
})
}
read()
})
.catch(error => {
if (error.name === 'AbortError') {
// 请求被取消
} else if (onError) {
onError(error.message)
}
})
return controller
},
cancelRequest(controller) {
if (controller) {
controller.abort()
}
},
getSessions(params) {
return request.get('/ai/sessions', { params })
},
getSessionDetail(sessionId, params) {
return request.get(`/ai/session/${sessionId}`, { params })
},
deleteSession(sessionId) {
return request.delete(`/ai/session/${sessionId}`)
}
}
```
## 关键点说明
### 1. 为什么"一下子输出出来"
问题在于 **`aiAPI.sendMessage`** **可能没有按 chunk 逐字调用** **`onMessage`**,而是:
- 要么收集完所有数据后一次性传给前端
- 要么每次收到多个字符才调用一次 `onMessage`
### 2. 修复方法
确保 **`onMessage`** **回调是每个字符块都被调用**,而不是批量处理:
```javascript
// ✅ 正确每个chunk立即传递
case 'content':
if (onMessage) onMessage(data.content) // 可能是1个字符也可能是多个
break
// ❌ 错误:收集后一次性传递
case 'content':
buffer += data.content
if (buffer.length >= 10) { // 等积累到一定量才发送
onMessage(buffer)
buffer = ''
}
break
```
### 3. 打字机效果的问题
即使 `onMessage` 被正确调用,您代码中的打字机效果 `startTypingEffect` **每次只添加 1-3 个字符**第864行这意味着如果 `onMessage` 一次收到超过 3 个字符,就会出现跳跃感。
建议检查 `onMessage` 回调是否真的被**逐字调用**
```javascript
// 在 QAModule.vue 的 handleSend 中添加调试:
onMessage: (contentChunk) => {
currentAIMessage.value += contentChunk
console.log(`📥 chunk长度: ${contentChunk.length}, 内容: "${contentChunk}"`)
// ...
}
```
如果调试日志显示 `chunk长度` 经常大于 3那打字效果就会显得不流畅。
您需要确认 `@/api/ai.js` 文件的存在和实现,或者如果有其他 API 文件路径,请告诉我它的位置。