☰
Codex 100个真实案例 - 用AI做智能客服聊天机器人(意图识别+多轮对话)
2026/9/26 11:45:59 网站建设 项目流程

1. 为什么客服机器人总是“听不懂人话”

做智能客服聊天机器人,最让人头疼的不是接不上大模型,而是用户一句“我上周买的那单怎么还没到”,系统直接回一句“请问您要咨询什么业务”。意图识别没命中,多轮对话上下文丢了,槽位提取全靠正则硬扛,最后用户骂骂咧咧点了转人工。

我试过把意图识别、槽位提取、多轮对话状态管理拆成三个独立模块,每个模块单独调优,效果比塞进一个大 prompt 里稳定得多。这套思路在电商客服场景里尤其明显:查订单、退换货、产品咨询、投诉建议、打招呼、告别,六类意图覆盖了 80% 的常见问题。剩下的长尾问题交给 FAQ 知识库做模糊匹配,匹配不上再转人工。

这篇文章要交付的是一条完整链路:用 Codex 生成意图识别引擎(TF-IDF + 贝叶斯分类器 + 关键词精确匹配三重评分),用槽位提取器从用户输入里抓订单号、手机号、退货原因,用对话状态机维护多轮上下文,最后通过 TaoToken 统一 Key 和 API 通道把模型调用接进来。适合正在做客服自动化、想跑通意图分类到上下文续接完整流程的开发者。

整个项目骨架用 Node.js + Express + Socket.IO 做后端,Vue 3 做 Web Widget,SQLite 存对话日志。下面从环境准备开始,一步步把可复制的配置和验证动作给出来。

2. TaoToken 前置:统一 Key 与 API 通道配置

在写业务代码之前,先把模型调用通道配好。TaoToken 的作用是提供一个统一的 API 入口,你不用在代码里硬编码多个厂商的 Key,也不用为每个模型单独写适配层。官网地址是 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,API 端点是 https://taotoken.net/api 。

2.1 获取 API Key

登录后进入控制台,在 API Keys 页面创建一个新 Key。建议按项目命名,比如smart-chatbot-dev,方便后续区分环境。创建后立即复制保存,页面刷新后不会再显示完整 Key。

2.2 config.toml 骨架

Codex 项目通常用config.toml管理模型配置。下面是一个可复制的骨架,把api_key替换成你刚创建的值:

# config.toml - TaoToken 统一通道配置 [model] provider = "taotoken" base_url = "https://taotoken.net/api" api_key = "sk-你的TaoTokenKey" default_model = "gpt-4o-mini" timeout = 30 [model.params] temperature = 0.3 max_tokens = 1024 top_p = 0.9 [intent] confidence_threshold = 0.3 unknown_retry_limit = 3 [dialog] session_timeout_minutes = 5 max_turns = 20

temperature设 0.3 是因为客服场景需要稳定输出,不需要太多创造性。confidence_threshold设 0.3 是意图识别的置信度阈值,低于这个值就返回 unknown,触发转人工建议。

2.3 环境变量注入

不要把 Key 直接写进代码提交到仓库。用.env文件管理:

# .env TAOTOKEN_API_KEY=sk-你的TaoTokenKey TAOTOKEN_BASE_URL=https://taotoken.net/api PORT=3200

然后在server/src/app.js里通过dotenv加载:

require('dotenv').config(); const apiKey = process.env.TAOTOKEN_API_KEY; const baseUrl = process.env.TAOTOKEN_BASE_URL;

这样本地开发和线上部署可以用不同的 Key,互不干扰。

3. 可复制配置:意图识别引擎与槽位提取器

3.1 意图定义文件 intents.json

先让 Codex 生成意图定义。每个意图包含tag、patterns(训练样本)、responses(回复模板)、required_slots(必需槽位)。下面是六类核心意图的配置:

{ "intents": [ { "tag": "greeting", "patterns": ["你好", "您好", "在吗", "有人吗", "客服在吗", "请问有人吗"], "responses": ["您好!我是智能客服小助手,请问有什么可以帮您的?"], "required_slots": [] }, { "tag": "query_order", "patterns": ["查订单", "我的订单", "订单状态", "物流信息", "快递到哪了", "什么时候发货", "包裹在哪"], "responses": ["好的,请提供您的订单号,我来帮您查询。"], "required_slots": ["order_id"] }, { "tag": "return_goods", "patterns": ["退货", "换货", "退款", "我要退", "申请退货", "东西有问题想退", "七天无理由退货"], "responses": ["了解,请问您要退货的订单号是多少?退货原因是什么?"], "required_slots": ["order_id", "reason"] }, { "tag": "product_inquiry", "patterns": ["这个产品怎么样", "有什么推荐", "产品介绍", "多少钱", "价格", "有没有优惠", "库存"], "responses": ["请问您想了解哪款产品?可以告诉我产品名称或型号。"], "required_slots": ["product_name"] }, { "tag": "complaint", "patterns": ["投诉", "我要投诉", "差评", "服务太差", "态度不好", "太慢了", "不满意"], "responses": ["非常抱歉给您带来不好的体验,请详细描述您遇到的问题。"], "required_slots": ["description"] }, { "tag": "farewell", "patterns": ["谢谢", "感谢", "拜拜", "再见", "没有问题了", "好的谢谢"], "responses": ["不客气!祝您生活愉快,有问题随时找我。"], "required_slots": [] } ] }

每个意图至少 10 条训练样本,样本越贴近真实用户表达,识别率越高。

3.2 意图识别引擎核心逻辑

意图识别用三重评分:关键词精确匹配权重 0.2,TF-IDF 相似度权重 0.4,贝叶斯分类器权重 0.4。综合得分低于阈值返回 unknown。

// server/src/intent-engine.js const natural = require('natural'); const fs = require('fs'); const path = require('path'); class IntentEngine { constructor() { this.tfidf = new natural.TfIdf(); this.classifier = new natural.BayesClassifier(); this.intents = []; this.confidenceThreshold = 0.3; this.initialized = false; } async init() { const dataPath = path.join(__dirname, '../../data/intents.json'); const rawData = fs.readFileSync(dataPath, 'utf-8'); const { intents } = JSON.parse(rawData); this.intents = intents; intents.forEach((intent) => { const document = intent.patterns.join(' '); this.tfidf.addDocument(document); intent.patterns.forEach(pattern => { this.classifier.addDocument(pattern, intent.tag); }); }); this.classifier.train(); this.initialized = true; console.log(`意图引擎初始化完成,已加载 ${intents.length} 个意图`); } recognize(userInput) { if (!this.initialized) throw new Error('意图引擎未初始化'); const input = userInput.trim().toLowerCase(); const exactMatch = this._exactMatch(input); if (exactMatch && exactMatch.confidence > 0.8) return exactMatch; const tfidfResult = this._tfidfMatch(input); const bayesResult = this._bayesMatch(input); const combinedScores = this._combineScores(exactMatch, tfidfResult, bayesResult); let bestIntent = null; let bestScore = 0; for (const [tag, score] of Object.entries(combinedScores)) { if (score > bestScore) { bestScore = score; bestIntent = tag; } } if (bestScore < this.confidenceThreshold) { return { intent: 'unknown', confidence: bestScore, responses: ['抱歉,我没有理解您的意思。您可以换个说法试试,或者输入"转人工"联系人工客服。'], required_slots: [] }; } const intentData = this.intents.find(i => i.tag === bestIntent); return { intent: bestIntent, confidence: bestScore, responses: intentData.responses, required_slots: intentData.required_slots || [] }; } _exactMatch(input) { let bestMatch = null; let bestScore = 0; for (const intent of this.intents) { for (const pattern of intent.patterns) { if (input.includes(pattern.toLowerCase())) { const score = pattern.length / Math.max(input.length, 1); if (score > bestScore) { bestScore = Math.min(score * 1.5, 1.0); bestMatch = { intent: intent.tag, confidence: bestScore, responses: intent.responses, required_slots: intent.required_slots || [] }; } } } } return bestMatch; } _tfidfMatch(input) { const scores = {}; this.tfidf.tfidfs(input, (index, measure) => { const tag = this.intents[index].tag; scores[tag] = measure; }); const maxScore = Math.max(...Object.values(scores), 1); for (const tag of Object.keys(scores)) { scores[tag] = scores[tag] / maxScore; } return scores; } _bayesMatch(input) { const classifications = this.classifier.getClassifications(input); const scores = {}; classifications.forEach(c => { scores[c.label] = c.value; }); const maxScore = Math.max(...Object.values(scores), 0.001); for (const tag of Object.keys(scores)) { scores[tag] = scores[tag] / maxScore; } return scores; } _combineScores(exactMatch, tfidfScores, bayesScores) { const combined = {}; const allTags = new Set([...Object.keys(tfidfScores), ...Object.keys(bayesScores)]); for (const tag of allTags) { const tfidfScore = tfidfScores[tag] || 0; const bayesScore = bayesScores[tag] || 0; const exactScore = (exactMatch && exactMatch.intent === tag) ? exactMatch.confidence : 0; combined[tag] = exactScore * 0.2 + tfidfScore * 0.4 + bayesScore * 0.4; } return combined; } } module.exports = IntentEngine;

3.3 槽位提取器

槽位提取用正则表达式 + 枚举值匹配 + 自由文本三种方式。订单号匹配ORD开头的编号,手机号匹配 11 位数字,退货原因从预定义枚举里找。

// server/src/slot-extractor.js const fs = require('fs'); const path = require('path'); class SlotExtractor { constructor() { const dataPath = path.join(__dirname, '../../data/slots.json'); const rawData = fs.readFileSync(dataPath, 'utf-8'); this.slotDefinitions = JSON.parse(rawData).slots; } extract(input, requiredSlots) { const extracted = {}; const missing = []; for (const slotName of requiredSlots) { const slotDef = this.slotDefinitions[slotName]; if (!slotDef) continue; const value = this._extractSlot(input, slotDef); if (value !== null) { extracted[slotName] = value; } else { missing.push(slotName); } } return { extracted, missing }; } _extractSlot(input, slotDef) { switch (slotDef.type) { case 'regex': return this._extractByRegex(input, slotDef.pattern); case 'enum': return this._extractByEnum(input, slotDef.values); case 'free_text': return this._extractFreeText(input, slotDef.min_length || 2); default: return null; } } _extractByRegex(input, pattern) { const regex = new RegExp(pattern, 'i'); const match = input.match(regex); return match ? match[0] : null; } _extractByEnum(input, values) { for (const value of values) { if (input.includes(value)) return value; } return null; } _extractFreeText(input, minLength) { const cleaned = input.replace(/你好|请问|帮我|我想|我要|麻烦/g, '').trim(); return cleaned.length >= minLength ? cleaned : null; } getPrompt(slotName) { const slotDef = this.slotDefinitions[slotName]; return slotDef ? slotDef.prompt : '请提供更多信息。'; } getSlotDisplayName(slotName) { const slotDef = this.slotDefinitions[slotName]; return slotDef ? slotDef.name : slotName; } } module.exports = SlotExtractor;

对应的slots.json定义:

{ "slots": { "order_id": { "name": "订单号", "type": "regex", "pattern": "ORD[0-9]{8,12}", "prompt": "请提供您的订单号(ORD开头的编号)", "examples": ["ORD20250001", "ORD202501150023"] }, "phone": { "name": "手机号", "type": "regex", "pattern": "1[3-9]\\d{9}", "prompt": "请提供您的手机号码", "examples": ["13800138000"] }, "product_name": { "name": "产品名称", "type": "enum", "values": ["蓝牙耳机", "充电宝", "手机壳", "数据线", "键盘", "鼠标"], "prompt": "请问您想了解哪款产品?", "examples": ["蓝牙耳机", "充电宝"] }, "reason": { "name": "退货原因", "type": "enum", "values": ["质量问题", "尺寸不合适", "不喜欢", "发错货", "商品损坏", "描述不符", "其他"], "prompt": "请问退货原因是什么?", "examples": ["质量问题", "不喜欢"] }, "description": { "name": "问题描述", "type": "free_text", "min_length": 5, "prompt": "请详细描述您遇到的问题", "examples": ["快递太慢了等了一周还没到"] } } }

4. 多轮对话状态机与验证请求

4.1 对话状态流转

多轮对话的核心是状态机。四个状态:IDLE(空闲,等待用户输入)、COLLECTING(正在收集槽位)、CONFIRMING(等待用户确认)、TRANSFERRING(正在转人工)。用户说“我要退货”但没给订单号时,状态从 IDLE 切到 COLLECTING,机器人主动追问订单号;订单号和退货原因都拿到后,切到 CONFIRMING 让用户确认;用户确认后执行退货逻辑,状态回到 IDLE。

// server/src/dialog-manager.js const { v4: uuidv4 } = require('uuid'); const IntentEngine = require('./intent-engine'); const SlotExtractor = require('./slot-extractor'); const DialogState = { IDLE: 'idle', COLLECTING: 'collecting', CONFIRMING: 'confirming', TRANSFERRING: 'transferring' }; class DialogManager { constructor() { this.intentEngine = new IntentEngine(); this.slotExtractor = new SlotExtractor(); this.sessions = new Map(); this.sessionTimeout = 5 * 60 * 1000; this._startCleanupTimer(); } async init() { await this.intentEngine.init(); console.log('对话管理器初始化完成'); } async processMessage(sessionId, userMessage) { let session = this.sessions.get(sessionId); if (!session) session = this._createSession(sessionId); session.lastActive = Date.now(); session.history.push({ role: 'user', content: userMessage, timestamp: Date.now() }); if (this._isTransferRequest(userMessage)) { return this._handleTransfer(session); } let reply; switch (session.state) { case DialogState.IDLE: reply = await this._handleIdleState(session, userMessage); break; case DialogState.COLLECTING: reply = await this._handleCollectingState(session, userMessage); break; case DialogState.CONFIRMING: reply = await this._handleConfirmingState(session, userMessage); break; default: reply = '系统异常,请重新开始对话。'; session.state = DialogState.IDLE; } session.history.push({ role: 'bot', content: reply, timestamp: Date.now() }); return { reply, state: session.state, sessionId: session.id, shouldTransfer: session.state === DialogState.TRANSFERRING, context: { intent: session.currentIntent, slots: session.filledSlots, turn: session.turnCount } }; } async _handleIdleState(session, userMessage) { const result = this.intentEngine.recognize(userMessage); session.currentIntent = result.intent; session.turnCount++; if (result.intent === 'unknown') { session.unknownCount++; if (session.unknownCount >= 3) { return '看起来我暂时无法理解您的问题,要帮您转人工客服吗?输入"转人工"即可。'; } return this._randomPick(result.responses); } session.unknownCount = 0; if (result.required_slots && result.required_slots.length > 0) { session.requiredSlots = [...result.required_slots]; session.filledSlots = {}; const extraction = this.slotExtractor.extract(userMessage, session.requiredSlots); Object.assign(session.filledSlots, extraction.extracted); if (extraction.missing.length > 0) { session.state = DialogState.COLLECTING; session.pendingSlots = extraction.missing; const nextSlot = session.pendingSlots[0]; const prompt = this.slotExtractor.getPrompt(nextSlot); return `${this._randomPick(result.responses)}\n${prompt}`; } else { return this._generateConfirmation(session); } } return this._randomPick(result.responses); } async _handleCollectingState(session, userMessage) { session.turnCount++; const intentCheck = this.intentEngine.recognize(userMessage); if (intentCheck.intent !== 'unknown' && intentCheck.intent !== session.currentIntent && intentCheck.confidence > 0.7) { session.state = DialogState.IDLE; return this._handleIdleState(session, userMessage); } const extraction = this.slotExtractor.extract(userMessage, session.pendingSlots); Object.assign(session.filledSlots, extraction.extracted); if (extraction.missing.length > 0) { session.pendingSlots = extraction.missing; const nextSlot = session.pendingSlots[0]; const slotName = this.slotExtractor.getSlotDisplayName(nextSlot); const prompt = this.slotExtractor.getPrompt(nextSlot); if (session.lastAskedSlot === nextSlot) { session.slotRetryCount++; if (session.slotRetryCount >= 3) { return `看起来我们在${slotName}这里遇到了困难,要转人工客服帮您处理吗?输入"转人工"即可。`; } return `抱歉,我没能识别出您的${slotName}。${prompt}`; } session.lastAskedSlot = nextSlot; session.slotRetryCount = 0; return prompt; } return this._generateConfirmation(session); } async _handleConfirmingState(session, userMessage) { const positive = ['是', '对', '确认', '没错', '好的', '可以', 'yes']; const negative = ['不是', '不对', '取消', '重新', '不', 'no']; const input = userMessage.trim().toLowerCase(); if (positive.some(w => input.includes(w))) { const result = this._executeBusiness(session); session.state = DialogState.IDLE; session.currentIntent = null; session.filledSlots = {}; return result; } if (negative.some(w => input.includes(w))) { session.state = DialogState.IDLE; session.filledSlots = {}; return '好的,已取消。请问还有什么可以帮您的?'; } return '请确认以上信息是否正确?回复"确认"继续,或回复"取消"重新输入。'; } _generateConfirmation(session) { session.state = DialogState.CONFIRMING; let confirmMsg = '请确认以下信息:\n'; for (const [slot, value] of Object.entries(session.filledSlots)) { const displayName = this.slotExtractor.getSlotDisplayName(slot); confirmMsg += `- ${displayName}:${value}\n`; } confirmMsg += '\n信息正确请回复"确认",需要修改请回复"取消"。'; return confirmMsg; } _executeBusiness(session) { const intent = session.currentIntent; const slots = session.filledSlots; switch (intent) { case 'query_order': return `订单 ${slots.order_id} 的状态:\n- 状态:已发货\n- 快递公司:顺丰速运\n- 预计送达:明天下午`; case 'return_goods': return `退货申请已提交!\n- 订单号:${slots.order_id}\n- 退货原因:${slots.reason}\n- 处理时间:1-3个工作日`; case 'product_inquiry': return `${slots.product_name} 产品信息:\n- 价格:¥199.00\n- 库存:充足\n- 评分:4.8/5.0`; case 'complaint': return `投诉已记录,工单号:TK${Date.now().toString().slice(-8)}\n- 问题描述:${slots.description}\n- 处理时限:24小时内回复`; default: return '处理完成!还有其他可以帮您的吗?'; } } _isTransferRequest(message) { const keywords = ['转人工', '人工客服', '真人客服', '找人工', '活人']; return keywords.some(k => message.includes(k)); } _handleTransfer(session) { session.state = DialogState.TRANSFERRING; return { reply: '好的,正在为您转接人工客服,请稍等...\n预计等待时间:1-3分钟', state: DialogState.TRANSFERRING, sessionId: session.id, shouldTransfer: true, context: { intent: session.currentIntent, slots: session.filledSlots, history: session.history } }; } _createSession(sessionId) { const session = { id: sessionId || uuidv4(), state: DialogState.IDLE, currentIntent: null, requiredSlots: [], pendingSlots: [], filledSlots: {}, history: [], turnCount: 0, unknownCount: 0, slotRetryCount: 0, lastAskedSlot: null, createdAt: Date.now(), lastActive: Date.now() }; this.sessions.set(session.id, session); return session; } _randomPick(arr) { return arr[Math.floor(Math.random() * arr.length)]; } _startCleanupTimer() { setInterval(() => { const now = Date.now(); for (const [id, session] of this.sessions) { if (now - session.lastActive > this.sessionTimeout) { this.sessions.delete(id); } } }, 60 * 1000); } } module.exports = { DialogManager, DialogState };

4.2 验证请求:跑通多轮对话

启动服务后,用 Socket.IO 客户端模拟一次完整的退货对话。先安装依赖:

cd server npm install express socket.io natural better-sqlite3 uuid cors dotenv

启动服务:

node src/app.js

看到智能客服服务已启动: http://localhost:3200说明服务正常。然后用一个简单的测试脚本验证多轮对话:

// test-dialog.js const { io } = require('socket.io-client'); const socket = io('http://localhost:3200'); socket.on('connect', () => { console.log('已连接到客服服务'); socket.emit('chat:join', { sessionId: 'test-session-001' }); }); socket.on('chat:message', (data) => { console.log(`[${data.role}] ${data.content}`); }); // 模拟多轮对话 setTimeout(() => { console.log('\n--- 第1轮:用户说退货 ---'); socket.emit('chat:message', { message: '我要退货', sessionId: 'test-session-001' }); }, 1000); setTimeout(() => { console.log('\n--- 第2轮:用户提供订单号 ---'); socket.emit('chat:message', { message: '订单号是 ORD202501150023', sessionId: 'test-session-001' }); }, 3000); setTimeout(() => { console.log('\n--- 第3轮:用户提供退货原因 ---'); socket.emit('chat:message', { message: '质量问题', sessionId: 'test-session-001' }); }, 5000); setTimeout(() => { console.log('\n--- 第4轮:用户确认 ---'); socket.emit('chat:message', { message: '确认', sessionId: 'test-session-001' }); }, 7000); setTimeout(() => { socket.disconnect(); process.exit(0); }, 9000);

预期输出:

[bot] 了解,请问您要退货的订单号是多少?退货原因是什么? [bot] 请提供您的订单号(ORD开头的编号) [bot] 请确认以下信息: - 订单号:ORD202501150023 - 退货原因:质量问题 信息正确请回复"确认",需要修改请回复"取消"。 [bot] 退货申请已提交! - 订单号:ORD202501150023 - 退货原因:质量问题 - 处理时间:1-3个工作日

如果第 2 轮没有正确提取订单号,检查slots.json里的正则ORD[0-9]{8,12}是否匹配你的订单号格式。如果第 3 轮没有识别“质量问题”,检查reason枚举值里是否包含这个词。

5. 本篇常见错排查

5.1 意图识别总是返回 unknown

先看置信度阈值。confidenceThreshold默认 0.3,如果训练样本太少或用户表达差异大,综合得分可能低于 0.3。把阈值调到 0.2 试试,但不要低于 0.15,否则容易误判。

再看训练样本。每个意图至少 10 条样本,且要覆盖不同说法。比如“查订单”这个意图,除了“查订单”“我的订单”,还要加“快递到哪了”“包裹在哪”“什么时候发货”这类口语化表达。

5.2 槽位提取失败

订单号提取失败最常见的原因是正则不匹配。如果你的订单号格式是ORD-2025-001,正则要改成ORD-?[0-9]{4}-?[0-9]{3,}。手机号提取失败检查是否带了国家码+86,如果有,正则要加(\+86)?前缀。

枚举值提取失败通常是用户说了同义词。比如用户说“东西坏了”,但枚举里只有“质量问题”。解决办法是在枚举值里加同义词,或者在提取前做一次同义词映射。

5.3 多轮对话上下文丢失

如果用户在第 2 轮提供了订单号,但机器人还在问订单号,说明session.pendingSlots没有正确更新。检查_handleCollectingState里extraction.missing的逻辑:如果missing为空,应该进入确认状态,而不是继续追问。

另一个常见问题是会话超时。默认 5 分钟无操作会清理会话,如果测试时间隔太久,会话被清理了,上下文自然丢失。测试时把sessionTimeout临时调大,或者确保在 5 分钟内完成对话。

5.4 Socket.IO 连接失败

检查app.js里的 CORS 配置。开发环境用origin: '*',生产环境要限制具体域名。如果前端报Access-Control-Allow-Origin错误,确认后端 CORS 中间件在 Socket.IO 之前加载。

端口冲突也是常见问题。3200 端口被占用时,改PORT环境变量,同时前端 Socket.IO 连接地址也要同步修改。

6. 接入文档与后续调优

意图识别和槽位提取跑通后,下一步是把 FAQ 知识库接进来。FAQ 用 TF-IDF 做模糊匹配,相似度超过 0.7 直接返回答案,不用走意图识别。这样常见问题(比如“怎么查物流”“支持哪些支付方式”)可以秒回,只有复杂问题才走多轮对话。

模型调用通道方面,TaoToken 的 API 端点https://taotoken.net/api兼容 OpenAI 格式,你可以直接把base_url指向它,用统一的 Key 调用不同模型。接入文档在 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,里面有完整的请求示例和参数说明。

如果你要长期跑编码任务或 Agent 流程,Coding Plan 页面 https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 有更详细的配额和模型选择说明。API Keys 管理在 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,建议按环境创建不同的 Key,方便排查问题时定位是哪个环境的调用。

调优方向有三个:一是扩充意图训练样本,把线上真实用户问法加进去;二是调整三重评分的权重,如果关键词匹配准确率高,可以把exactScore权重从 0.2 提到 0.3;三是给 FAQ 知识库加分类标签,用户问“退货”时优先匹配退货类 FAQ,减少无关答案的干扰。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询