fix(bridge): 商品链接兜底与强制检索
This commit is contained in:
@@ -10,6 +10,7 @@ const { createMetrics } = require('./metrics');
|
||||
const { auditAnswer } = require('./audit');
|
||||
const { markdownToHtml } = require('./markdown');
|
||||
const { guardReply } = require('./output-guard');
|
||||
const { appendMissingProductLinks } = require('./reply-links');
|
||||
const { quickClassify, detectEmotion, INTENTS } = require('./intent');
|
||||
const { createContextManager } = require('./context');
|
||||
const { createReranker } = require('./reranker');
|
||||
@@ -230,6 +231,26 @@ function createAgent(cfg, deps = {}) {
|
||||
stats.completionTokens += Number(response.usage.completion_tokens) || 0;
|
||||
stats.totalTokens += Number(response.usage.total_tokens) || 0;
|
||||
}
|
||||
// 兜底(旧 bridge 同款):product_search 意图下首轮 LLM 一个工具都没调时,
|
||||
// 丢弃它凭记忆编的回答,强制查一次真实商品结果再让它重答,避免幻觉商品/链接进入回复。
|
||||
if (!(response.toolCalls && response.toolCalls.length) && step === 0 && intent === INTENTS.PRODUCT_SEARCH && !stats.toolCalls.length) {
|
||||
const searchTool = byName.get('search_products');
|
||||
if (searchTool) {
|
||||
const started = Date.now();
|
||||
try {
|
||||
const forced = await searchTool.handler({ query: currentText, limit: 5 }, ctx);
|
||||
stats.toolCalls.push({ name: 'search_products', ms: Date.now() - started, ok: true, forced: true });
|
||||
stats.toolResults.push(forced);
|
||||
traceSteps.push({ step_type: 'tool', turn: step, tool_name: 'search_products', duration_ms: Date.now() - started });
|
||||
messages.push({ role: 'assistant', content: '', tool_calls: [{ id: 'forced-search-products', type: 'function', function: { name: 'search_products', arguments: JSON.stringify({ query: currentText, limit: 5 }) } }] });
|
||||
messages.push({ role: 'tool', tool_call_id: 'forced-search-products', content: markToolResult('search_products', JSON.stringify(forced).slice(0, 6000)) });
|
||||
console.log(JSON.stringify({ ts: new Date().toISOString(), agent: 'forced_search_products', conversation: conv.uuid, ok: true }));
|
||||
continue;
|
||||
} catch (err) {
|
||||
console.log(JSON.stringify({ ts: new Date().toISOString(), agent: 'forced_search_products', conversation: conv.uuid, ok: false, error: err.message }));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (response.toolCalls && response.toolCalls.length) {
|
||||
messages.push({ role: 'assistant', content: response.content || '', tool_calls: response.toolCalls });
|
||||
for (const call of response.toolCalls) {
|
||||
@@ -272,6 +293,22 @@ function createAgent(cfg, deps = {}) {
|
||||
if (!finalContent) {
|
||||
finalContent = '抱歉,我这边暂时无法处理,请您稍后再试。';
|
||||
}
|
||||
// 确定性链接兜底(旧 bridge buildProductListText 同款):LLM 漏写工具返回的真实商品/类目 URL 时,
|
||||
// 由代码在回复末尾补齐,保证客户至少拿到真实可点链接(放在护栏前,补的链接同样过白名单校验)。
|
||||
try {
|
||||
const backfill = appendMissingProductLinks({
|
||||
content: finalContent,
|
||||
toolResults: stats.toolResults,
|
||||
lang: detectLanguage(currentText),
|
||||
html: /<(p|br|a|ul|ol|strong|div)\b/i.test(finalContent),
|
||||
});
|
||||
if (backfill.appended) {
|
||||
finalContent = backfill.content;
|
||||
console.log(JSON.stringify({ ts: new Date().toISOString(), agent: 'link_backfill', conversation: conv.uuid, appended: backfill.appended }));
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(JSON.stringify({ ts: new Date().toISOString(), warn: `link backfill failed: ${err.message}` }));
|
||||
}
|
||||
// collect_info 兜底:消息含明显 lead 信息但 LLM 没调 collect_info 时,补抽槽位
|
||||
if (buyingIntent && leadStore && !(stats.toolCalls || []).some((t) => t.name === 'collect_info')) {
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user