【超详细】微信小程序对接讯飞星火知识库:文档上传、向量化、萃取 QA、流式问答从零到上线
摘要 :本文以可运行的微信小程序项目
xinghuo为例,按 CSDN 教程体例,逐步拆解 讯飞 星火知识库 ChatDoc API 的完整链路:鉴权 → 文档上传 → 向量化轮询 → 知识库 → 文档萃取(QA)→ WebSocket 流式问答 。包含官方接口请求/响应示例、与 Java/Python Demo 对照、完整小程序源码说明 、域名配置、以及笔者在实际开发中遇到的 405 / 60005 / 域名不合法 等问题的排查过程。
建议收藏,适合准备做 RAG、企业知识库问答、展会手册/ PDF 智能客服的开发者。
标签 :微信小程序 讯飞星火 星火知识库 ChatDoc RAG 文档问答 WebSocket 知识萃取
官方文档 :星火知识库 API 文档
官方 Python Demo :chatdoc-api-python-demo.zip
目录
- 零、读完本文你能做什么
- [一、先搞清楚:网页上传 ≠ API 闭环](#一、先搞清楚:网页上传 ≠ API 闭环)
- [二、核心概念:fileId、repoId、libId 一文讲透](#二、核心概念:fileId、repoId、libId 一文讲透)
- 三、环境准备与开通(带操作清单)
- [四、鉴权算法:逐行实现 + 两大坑](#四、鉴权算法:逐行实现 + 两大坑)
- [五、接口详解①:文档上传 file/upload](#五、接口详解①:文档上传 file/upload)
- [六、接口详解②:文档状态 file/status(向量化)](#六、接口详解②:文档状态 file/status(向量化))
- [七、接口详解③:知识库 repo](#七、接口详解③:知识库 repo)
- [八、接口详解④:文档萃取 qa/extract 全链路](#八、接口详解④:文档萃取 qa/extract 全链路)
- [九、接口详解⑤:WebSocket 文档问答](#九、接口详解⑤:WebSocket 文档问答)
- [十、接口详解⑥:文档列表 file/list](#十、接口详解⑥:文档列表 file/list)
- 十一、端到端时序:上传→萃取→问答
- 十二、微信小程序项目结构
- [十三、配置文件 config.js 逐项说明](#十三、配置文件 config.js 逐项说明)
- [十四、utils 工具层源码精读](#十四、utils 工具层源码精读)
- [十五、页面层:文档列表页 files](#十五、页面层:文档列表页 files)
- [十六、页面层:问答页 index](#十六、页面层:问答页 index)
- 十七、微信域名与开发者工具设置
- 十八、错误大全:现象→原因→解决
- [十九、自测 Checklist](#十九、自测 Checklist)
- 二十、上线与安全建议
- 二十一、总结
- [附录 A:官方错误码表](#附录 A:官方错误码表)
- [附录 B:完整项目目录树](#附录 B:完整项目目录树)
零、读完本文你能做什么
完成本文实践后,你可以:
- 在微信小程序 里选择 PDF/Word,调用 API 上传到讯飞知识库(不依赖 chatdoc 网页后台手工传文件)。
- 自动轮询向量化 ,直到文档状态为
vectored。 - 自动发起文档萃取 ,生成 QA 对,并
qa/apply写入知识库(无 repoId 时可自动建库)。 - 在聊天页基于该文档 WebSocket 流式问答 ,支持多轮对话、
qaMode: MIX混合检索。 - 用 file/list 核对当前
APP_ID下有哪些文档,避免「无文件权限」。
最终效果类似:上传《参展商手册》→ 自动萃取几十上百条 QA → 用户问「如何报名参展」→ 基于手册流式回答。
一、先搞清楚:网页上传 ≠ API 闭环
1.1 两种上传方式对比
| 对比项 | 网页 chatdoc.xfyun.cn | API file/upload(本文) |
|---|---|---|
| 操作入口 | 浏览器可视化 | 小程序 / 服务端代码 |
| 归属关系 | 必须属于某个开放平台应用 | 同上,Header 里带 appId |
| 得到的 ID | 页面展示 文件ID | 响应 JSON 的 data.fileId |
| 是否自动萃取 | 网页可能有单独按钮,与 API 无关 | 需主动调 qa/extract |
| 适用场景 | 体验、运营手工维护 | 产品化、自动化、小程序闭环 |
1.2 为什么网页上的 fileId 在小程序里报错?
典型报错:
text
code: 60005
message: 您没有文件权限
原因 :你在 config.js 里填的 APP_ID(例如 d87f81de)与网页上传时登录账号绑定的不是同一个应用 。
讯飞侧权限模型是:fileId 从属于创建它的 appId,不能跨应用借用。
正确做法:
- 要么:网页与小程序统一同一个应用的 APPID;
- 要么:只在小程序里用 API 上传 ,用
file/list查本应用下的 fileId。
1.3 创建应用时没有「星火知识库」分类?
正常现象。流程是:
- 控制台 → 创建应用(分类选「企业服务 / 教育 / 其他」均可);
- 进入该应用 → 能力列表 → 开通 星火知识库 / 文档问答;
- 复制 APPID、APISecret 到
config.js。
详见 讯飞快速指引。
二、核心概念:fileId、repoId、libId 一文讲透
这是全文最容易踩坑的部分,请务必理解。
2.1 三个 ID 对照表
| 名称 | 示例 | 来源 | 用于 API 字段 | 常见误用 |
|---|---|---|---|---|
| fileId | a90c49dcc76f4a9a82bdcc562c512bb6 |
上传接口返回 / 文档列表 / 网页「文件ID」 | fileIds: ['...'] |
填进 REPO_ID → 报「知识库不存在」 |
| repoId | 8e9f5da278c24f08880e5b0380626b85(32位UUID) |
repo/create 或 repo/list |
repoId: '...' |
与 fileId 混用 |
| libId(网页) | 19855 |
URL doc-detail?libId=19855 |
不能用于 API | 当成 repoId |
2.2 问答时传 fileIds 还是 repoId?
官方要求:repoId、repoIds、fileIds 三者必填其一。
| 模式 | 配置 | 行为 |
|---|---|---|
| 单文档问答 | FILE_IDS: ['fileId'] |
只检索该 PDF |
| 知识库问答 | REPO_ID: 'repoId' |
检索库内多文档 + 已 apply 的 QA |
| 本项目存储 | wx.setStorageSync('ACTIVE_FILE_IDS') |
上传后自动写入,优先于 config |
本项目逻辑:若存在 ACTIVE_REPO_ID,问答只传 repoId ;否则传 fileIds。
2.3 文档状态机(必须等 vectored)
uploaded → texted → ocring → spliting → splited → vectoring → vectored
↓
failed
| 状态 | 含义 | 能否问答 | 能否萃取 |
|---|---|---|---|
| uploaded | 已上传 | ❌ | ❌ |
| vectoring | 向量化中 | ❌ | ❌ |
| vectored | 已向量化 | ✅ | ✅ |
| failed | 失败 | ❌ | ❌ |
上传接口不会立刻返回 vectored ,必须轮询 file/status。
三、环境准备与开通(带操作清单)
3.1 账号与资质
- 注册 讯飞开放平台
- 完成实名认证(部分能力需认证后可见)
- 创建应用,开通 星火知识库,领取免费额度
3.2 微信小程序
- 注册小程序,获取 AppID(微信的,与讯飞 APPID 不同)
- 安装 微信开发者工具
- 导入本项目,填写自己的讯飞
config.js
3.3 服务器域名(上线必做)
登录 微信公众平台 → 开发管理 → 开发设置 → 服务器域名:
| 类型 | 填写 |
|---|---|
| request 合法域名 | https://chatdoc.xfyun.cn |
| socket 合法域名 | wss://chatdoc.xfyun.cn |
开发调试 :开发者工具 → 详情 → 本地设置 → 勾选 「不校验合法域名...」 ;项目 project.private.config.json 中 "urlCheck": false。
【建议配图】微信公众平台「服务器域名」配置页截图。
3.4 下载官方 Demo 对照
Python Demo 中鉴权、上传、问答 URL 与本文一致,建议下载对照调试:
四、鉴权算法:逐行实现 + 两大坑
文档章节:二、鉴权认证
4.1 算法步骤(与 Java 官方一致)
text
输入:appId(字符串), secret(APISecret), timestamp(秒,long)
Step1: auth = MD5( appId + timestamp ) // 拼接后做 MD5,32位小写 hex,无盐
Step2: signature = Base64( HmacSHA1( auth, secret ) ) // auth 为消息,secret 为密钥
Python 官方写法 (Document_Q_And_A.py):
python
m2 = hashlib.md5()
data = bytes(self.appId + self.timeStamp, encoding="utf-8")
m2.update(data)
checkSum = m2.hexdigest()
signature = hmac.new(
self.apiSecret.encode('utf-8'),
signature_origin.encode('utf-8'),
digestmod=hashlib.sha1
).digest()
signature = base64.b64encode(signature).decode(encoding='utf-8')
4.2 坑①:WebSocket 的 signature 不要做 URL 编码
官方 Demo 注释原文:
python
# 使用urlencode会导致签名乱码
return self.originUrl + "?" + f'appId={self.appId}×tamp={self.timeStamp}&signature={signature}'
错误写法(会 405 Invalid Signature):
javascript
`signature=${encodeURIComponent(signature)}` // ❌
正确写法:
javascript
`wss://chatdoc.xfyun.cn/openapi/chat?appId=${appId}×tamp=${timestamp}&signature=${signature}`
4.3 坑②:secret 填错字段
| 控制台字段 | 是否用于 ChatDoc |
|---|---|
| APISecret | ✅ 填到 APP_SECRET |
| APIKey | ❌ 常用于其他星火接口,勿混淆 |
4.4 小程序完整实现(utils/auth.js 核心)
javascript
function getSignature(appId, secret, timestamp) {
const auth = md5(String(appId) + String(timestamp))
return hmacSha1Base64(auth, secret)
}
function getAuthHeaders(appId, secret) {
const timestamp = Math.floor(Date.now() / 1000)
return {
appId,
timestamp: String(timestamp),
signature: getSignature(appId, secret, timestamp)
}
}
function buildChatWsUrl(appId, secret) {
const timestamp = Math.floor(Date.now() / 1000)
const signature = getSignature(appId, secret, timestamp)
return `wss://chatdoc.xfyun.cn/openapi/chat?appId=${appId}×tamp=${timestamp}&signature=${signature}`
}
HTTP 类接口(上传、列表、萃取)在 Header 中带 appId、timestamp、signature 即可。
4.5 鉴权失败 HTTP 状态码
| HTTP Code | 说明 |
|---|---|
| 401 | 参数缺失、签名解析失败、签名校验失败 |
| 403 | 时间戳偏差超过 5 分钟 |
| 405 | 应用无效 / Invalid Signature |
五、接口详解①:文档上传 file/upload
文档:3.1 文档上传
5.1 基本信息
| 项 | 值 |
|---|---|
| URL | POST https://chatdoc.xfyun.cn/openapi/v1/file/upload |
| Content-Type | multipart/form-data |
| 鉴权 | Header:appId, timestamp, signature |
5.2 表单字段说明
| 字段 | 必须 | 值 | 说明 |
|---|---|---|---|
| file | 与 url 二选一 | 文件流 | 小程序 wx.uploadFile 的 name 必须为 file |
| fileType | Y | wiki |
固定值 |
| parseType | Y | AUTO |
智能判断是否 OCR;可选 TEXT / OCR |
| stepByStep | N | false |
true 时仅分片,需再调 embedding;一般传 false |
| url | N | 文件 URL | 与 file 二选一 |
| fileName | 用 url 时必传 | 带后缀文件名 | 本地 file 上传可不传 |
限制:doc/docx、pdf、md、txt ;≤ 20MB ;≤ 100 万字符。
5.3 成功响应示例
json
{
"code": 0,
"sid": "",
"desc": null,
"data": {
"fileId": "",
"quantity": "12",
"parseType": "TEXT"
}
}
请立即记录 data.fileId,后续所有接口都依赖它。
5.4 小程序:选文件 + 上传
选文件(从微信聊天记录选 PDF 等):
javascript
wx.chooseMessageFile({
count: 1,
type: 'file',
extension: ['pdf', 'doc', 'docx', 'txt', 'md'],
success(res) {
const file = res.tempFiles[0]
// file.path 临时路径
// file.name 文件名
// file.size 字节数,需 <= 20*1024*1024
}
})
上传 (utils/api.js → uploadFile):
javascript
wx.uploadFile({
url: 'https://chatdoc.xfyun.cn/openapi/v1/file/upload',
filePath: filePath,
name: 'file',
formData: {
fileType: 'wiki',
parseType: 'AUTO',
stepByStep: 'false'
},
header: getAuthHeaders(appId, secret),
success(res) {
const body = JSON.parse(res.data)
if (body.code === 0) {
const fileId = body.data.fileId
}
}
})
5.5 封装函数 uploadDocument(utils/file.js)
javascript
function uploadDocument(appId, secret, filePath, fileName) {
return uploadFile('/openapi/v1/file/upload', {
appId,
secret,
filePath,
formData: {}
}).then(data => ({
fileId: data.fileId,
parseType: data.parseType,
quantity: data.quantity,
fileName
}))
}
六、接口详解②:文档状态 file/status(向量化)
文档:3.2 文档状态查询
6.1 基本信息
| 项 | 值 |
|---|---|
| URL | POST https://chatdoc.xfyun.cn/openapi/v1/file/status |
| Content-Type | application/x-www-form-urlencoded |
| Body | fileIds=id1 或 fileIds=id1,id2 |
6.2 响应示例
json
{
"code": 0,
"desc": "成功",
"data": [
{
"fileId": "你的fileId",
"fileStatus": "vectoring"
}
]
}
6.3 轮询实现 waitUntilVectored
本项目策略:
- 每 3 秒 查一次;
- 最多 100 次(约 5 分钟);
vectored→ resolve;failed→ reject;否则setTimeout继续。
javascript
function waitUntilVectored(appId, secret, fileId, options = {}) {
const { interval = 3000, maxAttempts = 100, onProgress } = options
let attempts = 0
const poll = () => {
attempts += 1
return getFileStatus(appId, secret, fileId).then(list => {
const item = list[0]
onProgress && onProgress(item)
if (item.fileStatus === 'vectored') return item
if (item.fileStatus === 'failed') throw new Error('文档处理失败')
if (attempts >= maxAttempts) throw new Error('处理超时')
return new Promise(r => setTimeout(() => r(poll()), interval))
})
}
return poll()
}
界面提示 :将 vectoring 显示为「向量化中」,避免用户以为卡死。
七、接口详解③:知识库 repo
文档:六、高级功能-知识库
7.1 何时需要知识库?
| 场景 | 是否需要 repoId |
|---|---|
| 只问一本 PDF | ❌,仅 fileIds 即可 |
| 多文档统一问答 | ✅ |
| 萃取后 qa/apply | ✅ 必填 (本项目无 repo 时自动 repo/create) |
7.2 创建知识库 repo/create
http
POST https://chatdoc.xfyun.cn/openapi/v1/repo/create
Content-Type: application/json
{
"repoName": "2026x'x'x",
"repoDesc": "手册",
"repoTags": "展会"
}
响应 data 即为 repoId(字符串 UUID)。
7.3 添加文件 repo/file/add
json
{
"repoId": "xxx",
"fileIds": [""]
}
注意:文件须 vectored;单次最多 20 个 fileId。
7.4 小程序「创建知识库」
pages/files/files.js 中 onCreateRepo 弹窗输入名称 → 调用 createRepo → 结果存入 wx.setStorageSync('ACTIVE_REPO_ID', repoId)。
八、接口详解④:文档萃取 qa/extract 全链路
文档:四、高级功能-文档萃取
8.1 重要:上传 ≠ 萃取
很多开发者以为「上传并向量化」后,网页上看到的「萃取」会自动完成。
实际上:
- 向量化 :上传后服务端流水线自动做,查到
vectored即可问答「原文片段」; - 萃取 :必须额外调用
/qa/extract,才会生成 QA 对; - 生效 :萃取结果要通过
/qa/apply写入索引(且需要 repoId ),问答时qaMode: MIX才能用好 QA。
8.2 步骤 1:提交萃取任务
http
POST https://chatdoc.xfyun.cn/openapi/v1/qa/extract
Content-Type: application/json
请求体:
json
{
"fileId": "你的fileId",
"chunkSize": 2000,
"numPerChunk": 2,
"answerSize": 200,
"includeAnswer": true
}
| 参数 | 说明 |
|---|---|
| chunkSize | 按多少字符分块再抽 QA |
| numPerChunk | 每块约抽几个问题(非精确) |
| answerSize | 答案长度上限(非精确) |
前置条件:fileStatus === 'vectored'。
响应 data 为 taskId(也可后续用 fileId 查状态)。
8.3 步骤 2:轮询萃取状态
http
GET https://chatdoc.xfyun.cn/openapi/v1/qa/extract/status?fileId=xxx
Header 仍需鉴权。
| data 值 | 含义 |
|---|---|
| UNSTARTED | 未开始 |
| EXTRACTING | 萃取中(大 PDF 可能 数分钟) |
| EXTRACTED | 完成 |
| FAILED | 失败 |
本项目:每 4 秒轮询,最多 90 次。
8.4 步骤 3:获取萃取结果
http
GET https://chatdoc.xfyun.cn/openapi/v1/qa/extract/result?fileId=xxx
响应 data 为数组:
json
[
{
"id": "你的id",
"question": "参展商如何办理证件?",
"answer": "请携带......",
"chunkIndexReferences": [1, 2, 3],
"answerChunkIndexReferences": [1, 2, 3]
}
]
8.5 步骤 4:应用 QA 对(关键)
http
POST https://chatdoc.xfyun.cn/openapi/v1/qa/apply
json
{
"fileId": "你的fileId",
"repoId": "你的知识库ID",
"question": "参展商如何办理证件?",
"answer": "请携带......",
"embType": "QA"
}
fileId、repoId 均为必填。 本项目 ensureRepoId:若用户未配置 REPO_ID,自动 repo/create 再逐条 apply。
8.6 本项目自动化 runExtractPipeline(utils/extract.js)
text
startExtract
→ waitUntilExtracted
→ getExtractResult
→ ensureRepoId(无则 createRepo)
→ addFilesToRepo(忽略失败)
→ applyAllQaPairs(递归逐条 apply)
上传时 uploadAndProcess 在向量化、加库之后,若 config.extract.enabled === true 自动执行上述流程。
已有文档未萃取 :列表页点 「萃取」 按钮,单独执行 runExtractPipeline。
8.7 config.extract 配置项
javascript
extract: {
enabled: true, // 上传完成后是否自动萃取
chunkSize: 2000,
numPerChunk: 2, // 越大 QA 越多,耗时越长
answerSize: 200,
autoApply: true // 是否自动 qa/apply
}
九、接口详解⑤:WebSocket 文档问答
文档:3.3 文档问答
9.1 连接 URL
text
wss://chatdoc.xfyun.cn/openapi/chat?appId=xxx×tamp=xxx&signature=xxx
使用 wx.connectSocket({ url }),不要对 signature 做 encodeURIComponent。
9.2 连接成功后发送 JSON
json
{
"fileIds": ["a90c49dcc76f4a9a82bdcc562c512bb6"],
"messages": [
{ "role": "user", "content": "展会开放时间是?" }
],
"chatExtends": {
"spark": true,
"temperature": 0.5,
"retrievalFilterPolicy": "REGULAR",
"qaMode": "MIX"
}
}
多轮对话示例:
json
{
"messages": [
{ "role": "user", "content": "如何理赔?" },
{ "role": "assistant", "content": "此前回答的完整内容......" },
{ "role": "user", "content": "家属有什么福利?" }
]
}
9.3 流式响应字段
json
{
"code": 0,
"content": "根据手册,",
"sid": "870b299a48ba46b6b5e4dfbd99fe4497",
"status": 1
}
| status | 含义 | 客户端处理 |
|---|---|---|
| 0 | 首包 | 拼接 content |
| 1 | 中间包 | 继续拼接 |
| 2 | 结束 | 关闭连接,结束 loading |
| 99 | 文档引用 | fileRefer 为 Map,可选展示 |
9.4 chatExtends 参数说明
| 字段 | 说明 |
|---|---|
| spark | 无文档匹配时是否用大模型兜底 |
| temperature | (0,1],越大越随机 |
| retrievalFilterPolicy | STRICT / REGULAR / LENIENT / OFF |
| qaMode | QA_FIRST / QA_SUMMARY / MIX / WIKI_ONLY |
萃取后建议 qaMode: 'MIX',同时参考 QA 对与 wiki 分片。
9.5 utils/chatdoc.js 核心逻辑
javascript
const url = buildChatWsUrl(appId, secret)
const socketTask = wx.connectSocket({ url })
socketTask.onOpen(() => {
socketTask.send({ data: JSON.stringify(payload) })
})
socketTask.onMessage((res) => {
const data = JSON.parse(res.data)
if (data.code !== 0) { /* 405/60005 等 */ }
if (data.content) fullContent += data.content
if (data.status === 2) onComplete({ content: fullContent })
})
十、接口详解⑥:文档列表 file/list
文档:5.8 文档列表
10.1 请求
http
POST https://chatdoc.xfyun.cn/openapi/v1/file/list
Content-Type: application/json
{
"currentPage": 1,
"pageSize": 20,
"fileName": "",
"extName": "",
"fileStatus": ""
}
10.2 响应(节选)
json
{
"code": 0,
"data": {
"total": 3,
"rows": [
{
"fileId": "你的fileId",
"fileName": "2026xxxxxx.pdf",
"fileStatus": "vectored",
"extName": "pdf",
"quantity": 24
}
]
}
}
用途:确认 fileId 是否属于当前 APP_ID;复制 ID 到配置;查看是否已向量化。
十一、端到端时序:上传→萃取→问答
chatdoc.xfyun.cn 小程序 用户 chatdoc.xfyun.cn 小程序 用户 loop [每3秒] opt [配置了 REPO_ID] loop [每4秒] loop [每条 QA] 点击「上传文档」 POST /file/upload (multipart) fileId POST /file/status vectoring / vectored POST /repo/file/add POST /qa/extract GET /qa/extract/status EXTRACTING / EXTRACTED GET /qa/extract/result QA 列表 POST /qa/apply 存储 ACTIVE_FILE_IDS 输入问题 WebSocket /openapi/chat 流式 content (status 0,1,2)
十二、微信小程序项目结构
12.1 app.json 注册页面
json
{
"pages": [
"pages/index/index",
"pages/files/files",
"pages/logs/logs"
],
"window": {
"navigationBarTitleText": "星火知识库问答"
}
}
12.2 目录树(附录 B 有完整版)
text
xinghuo/
├── config.js # 讯飞凭证、萃取、问答参数
├── config.example.js
├── pages/
│ ├── index/ # 流式问答 UI
│ └── files/ # 上传、列表、萃取
└── utils/
├── auth.js # 鉴权
├── api.js # HTTP / 上传 / GET
├── file.js # 上传+向量化+总流程
├── extract.js # 萃取+apply
├── repo.js # 知识库
└── chatdoc.js # WebSocket 问答
十三、配置文件 config.js 逐项说明
javascript
module.exports = {
// 【必填】讯飞开放平台 → 星火知识库应用
APP_ID: '你的APPID',
APP_SECRET: '你的APISecret',
// 【可选】已有知识库 ID,上传后 repo/file/add
REPO_ID: '',
// 【可选】固定文档;留空则走上传+本地存储
FILE_IDS: [],
// 【萃取】上传并向量化完成后自动执行
extract: {
enabled: true,
chunkSize: 2000,
numPerChunk: 2,
answerSize: 200,
autoApply: true
},
// 【问答】WebSocket chatExtends
chatExtends: {
spark: true,
temperature: 0.5,
retrievalFilterPolicy: 'REGULAR',
qaMode: 'MIX'
}
}
本地存储键(代码内,无需手写 config):
| Key | 含义 |
|---|---|
ACTIVE_FILE_IDS |
当前问答用的 fileId 数组 |
ACTIVE_REPO_ID |
当前问答用的 repoId(优先于 FILE_IDS) |
十四、utils 工具层源码精读
14.1 api.js:三类 HTTP 封装
| 函数 | 用途 |
|---|---|
request |
JSON POST + 鉴权 Header |
requestForm |
表单 POST(file/status) |
requestGet |
GET + query(萃取状态/结果) |
uploadFile |
wx.uploadFile 封装 |
统一域名:https://chatdoc.xfyun.cn。
14.2 file.js:uploadAndProcess 总控
javascript
uploadDocument
→ waitUntilVectored
→ [可选] addFilesToRepo
→ [可选] runExtractPipeline
页面只调 uploadAndProcess,通过 onProgress 更新 uploadMessage 展示进度。
14.3 extract.js:萃取流水线
见第八章;applyAllQaPairs 顺序调用避免并发触发频控(错误码 68003 操作太过频繁)。
14.4 chatdoc.js:流式问答
注意 code !== 0 时即使 status === 2 也要走错误分支(业务错误包在最后一帧的情况)。
十五、页面层:文档列表页 files
15.1 功能清单
| 按钮/操作 | 调用 |
|---|---|
| 上传文档 | chooseMessageFile → uploadAndProcess |
| 创建知识库 | createRepo |
| 刷新列表 | getFileList |
| 萃取 | runExtractPipeline(仅 vectored) |
| 使用 | setStorage ACTIVE_FILE_IDS |
| 按知识库问答 | 清除 FILE_IDS,保留 REPO_ID |
| 去问答 | navigateBack → index |
15.2 上传进度文案示例
text
准备上传...
正在上传...
上传成功,正在向量化...
切分中 / 向量化中
正在加入知识库...
正在提交萃取任务...
萃取中,请稍候...
正在将 QA 写入知识库...
正在应用 QA 3/28...
【建议配图】文档列表页:上传中、列表展示 fileId 与状态、萃取按钮。
十六、页面层:问答页 index
16.1 发送流程
- 用户输入 →
_sendMessage getChatTargets()取 repoId 或 fileIds- 组装
messages(过滤 welcome、loading) chatWithKnowledge建立 WebSocketonChunk实时更新 assistant 气泡status === 2结束 loading
16.2 入口
底部 「文档列表」 → wx.navigateTo('/pages/files/files')。
十七、微信域名与开发者工具设置
17.1 两类域名缺一不可
| 能力 | 域名类型 | 域名 |
|---|---|---|
| 上传、列表、状态、萃取 | request | https://chatdoc.xfyun.cn |
| 问答 WebSocket | socket | wss://chatdoc.xfyun.cn |
只配 socket 不配 request → 上传报:
text
https://chatdoc.xfyun.cn 不在以下 request 合法域名列表中
17.2 开发者工具本地设置
- 右上角 详情
- 本地设置
- 勾选 不校验合法域名、web-view(业务域名)、TLS 版本以及 HTTPS 证书
- 编译
同时 project.private.config.json:
json
"setting": {
"urlCheck": false
}
十八、错误大全:现象→原因→解决
18.1 405 Invalid Signature
| 可能原因 | 解决 |
|---|---|
| APISecret 填错 | 对照控制台复制 |
| 用了 APIKey | 改用 APISecret |
| WebSocket URL 对 signature 做了 encode | 去掉 encodeURIComponent |
| 时间戳偏差 >5 分钟 | 同步系统时间 |
18.2 60005 无文件权限
| 可能原因 | 解决 |
|---|---|
| fileId 来自其他 APP_ID | 用 file/list 查本应用文档 |
| 网页上传与小程序不是同一应用 | 统一应用或 API 重新上传 |
18.3 知识库不存在
| 可能原因 | 解决 |
|---|---|
| 把 fileId 填进 REPO_ID | 改填 FILE_IDS |
| 把 libId=19855 填进 REPO_ID | 用 repo/list 拿 API 的 repoId |
18.4 request / socket 域名报错
见第十七章;真机预览必须在公众平台配置域名。
18.5 上传成功但问答无内容
| 可能原因 | 解决 |
|---|---|
| 未等 vectored 就问答 | 轮询完成后再问 |
| 未萃取且问题只匹配 QA | 开启萃取或改 qaMode 为 WIKI_ONLY |
| retrievalFilterPolicy 过严 | 改为 LENIENT 或 OFF |
18.6 萃取超时
| 可能原因 | 解决 |
|---|---|
| PDF 页数多 | 增大 maxAttempts;稍后点「萃取」重试 |
| 额度不足 | 控制台查看用量 |
十九、自测 Checklist
-
config.jsAPP_ID / APP_SECRET 正确 - 开发者工具已关闭域名校验或已配置域名
- 上传 5MB 以内 pdf 成功返回 fileId
- 列表页状态变为「可向量化问答」
- 萃取完成弹窗显示 QA 条数 > 0
- 问答页能流式输出
- 问一个手册里明确存在的问题,答案相关
- file/list 能看到刚上传的文件
二十、上线与安全建议
- APISecret 不要写死在小程序包内,生产环境用云函数/自有后端代理签名与上传。
- 上传+萃取耗时长,建议进度条、防重复点击、可取消(本项目可扩展
abort)。 qa/apply条数多时注意接口频率,必要时加 delay。- 个人主体小程序可能无法配置外部业务域名,需企业主体或中转服务。
- 涉政涉敏内容会返回 10019 / 10013 等,见附录 A。
二十一、总结
| 阶段 | 接口 | 小程序实现 |
|---|---|---|
| 鉴权 | Header / WSS query | utils/auth.js |
| 上传 | POST /file/upload | wx.uploadFile |
| 向量化 | POST /file/status | 轮询 waitUntilVectored |
| 知识库 | /repo/create, /repo/file/add | 可选 + 自动建库 |
| 萃取 | /qa/extract + status + result | runExtractPipeline |
| 应用 QA | /qa/apply | 逐条 apply |
| 问答 | WSS /openapi/chat | chatdoc.js 流式 UI |
核心认知再强调三遍:
- fileId 只属于一个讯飞 APP_ID;
- 上传不会自动萃取,必须调第四章接口;
- WebSocket 的 signature 不要 URL 编码。
按本文接口顺序实现,即可在小程序内完成与讯飞星火知识库一致的 上传 → 萃取 → 问答 全链路。
附录 A:官方错误码表
| 错误码 | 描述 | 处理 |
|---|---|---|
| 10019 | 问题或文段可能涉政 | 检查内容 |
| 10013 | 敏感违规 | 检查内容 |
| 10014 | 输出敏感 | 换问法 |
| 60001 | 文件类型不对 | 检查格式 |
| 60002 | 文件大小超限 | ≤20MB |
| 60005 | 无文件权限 | 检查 appId 与 fileId |
| 60014 | 问答未传文件 id | 检查入参 |
| 62001 | 未找到相关文本段 | 换问题或调低过滤阈值 |
| 68003 | 操作太频繁 | 降频 |
| 99999 | 内部错误 | 重试/工单 |