perf(bridge): 商品检索改用短关键词并恢复分阶段计时

This commit is contained in:
wangliang
2026-10-23 16:32:03 +08:00
parent a0d386b752
commit 0959777048
6 changed files with 174 additions and 12 deletions
+38
View File
@@ -357,3 +357,41 @@ test('agent: extracts lead fields when the LLM skips collect_info (company menti
assert.equal(twenty.calls.filter((c) => c[0] === 'createOpportunity').length, 1);
assert.ok(twenty.calls.some((c) => c[0] === 'createCompany' && c[1] === 'Leadfix GmbH'));
});
test('agent: product pre-retrieval sends cleaned keyword instead of the full sentence', async () => {
const queries = [];
const tools = [{
name: 'search_products',
description: 'fake',
parameters: { type: 'object', properties: {} },
handler: async (args) => {
queries.push(args.query);
return { products: [{ name: 'Steel Ring', price: '€2,50', stock: 10, url: 'https://www.yehwang.com/p/steel-ring.html' }], count: 1 };
},
}];
tools.leadStore = { get: async () => null, save: async () => null };
let sent = '';
let recorded = null;
const agent = createAgent({ sites: [], defaultSite: 'www' }, {
llm: {
enabled: true,
model: 'fake',
chat: async () => ({ content: 'Steel Ring — €2,50,库存 10 件。', toolCalls: [], usage: null }),
},
client: {
getMessages: async () => [{ id: 1, text_content: 'We need 500 stainless steel rings wholesale.', sender_type: 'contact', private: false }],
sendMessage: async (_uuid, content) => { sent = content; },
sendPrivateNote: async () => {},
},
tools,
verification: { getVerifiedEmail: async () => null, sendCode: async () => ({}), verifyCode: async () => ({}) },
metrics: { record: async (m) => { recorded = m; }, enabled: false },
contextManager: { compressHistory: async (_c, h) => h },
});
const conv = { uuid: 'conv-clean-1', inbox_id: '1', channel: 'livechat', contact: { type: 'visitor' } };
await agent.run(conv, { uuid: 'msg-clean-1', text_content: 'We need 500 stainless steel rings wholesale.', content: '...' });
assert.deepEqual(queries, ['stainless steel rings']);
assert.match(sent, /Steel Ring/);
assert.equal(typeof recorded.audit.timing.llm_ms, 'number');
assert.equal(typeof recorded.audit.timing.tool_ms, 'number');
});
+18 -1
View File
@@ -2,7 +2,7 @@
const test = require('node:test');
const assert = require('node:assert/strict');
const { translateProductQuery } = require('../src/product-query');
const { translateProductQuery, extractProductKeyword } = require('../src/product-query');
test('translateProductQuery: Chinese category alias -> English title', () => {
const entries = [
@@ -20,3 +20,20 @@ test('translateProductQuery: English query unchanged', () => {
assert.equal(out.translated, false);
assert.equal(out.query, 'hoop earrings');
});
test('extractProductKeyword: drops quantities/currency/transaction words', () => {
assert.equal(extractProductKeyword('We need 500 stainless steel rings wholesale under 10 dollars'), 'stainless steel rings');
assert.equal(extractProductKeyword('500 rings'), 'rings');
assert.equal(extractProductKeyword('do you have gold hoop earrings?'), 'gold hoop earrings');
assert.equal(extractProductKeyword('do you accept returns on stainless steel rings'), 'stainless steel rings');
});
test('extractProductKeyword: caps at 4 tokens, keeps CJK', () => {
assert.equal(extractProductKeyword('neo stainless steel rings 500 pcs'), 'neo stainless steel rings');
assert.equal(extractProductKeyword('简约不锈钢戒指'), '简约不锈钢戒指');
});
test('extractProductKeyword: pure quantity/transaction returns empty', () => {
assert.equal(extractProductKeyword('500 pcs wholesale'), '');
assert.equal(extractProductKeyword(''), '');
});
+12
View File
@@ -91,6 +91,18 @@ test('search_products: maps RPC items and caps results', async () => {
assert.equal(rpc.calls[0].method, '/product/list');
});
test('search_products: sends cleaned short keyword to catalog RPC for long queries', async () => {
const rpc = fakeRpc(() => ({ data: { list: [{ spuName: 'Steel Ring', minPriceWithCurrency: '€2,50', spuId: 1 }] } }));
const tools = createTools(baseCfg, { rpc, twenty: fakeTwenty() });
const tool = tools.find((t) => t.name === 'search_products');
const out = await tool.handler({ query: 'We need 500 stainless steel rings wholesale under 10 dollars', limit: 3 }, { verified: false });
const cmd = rpc.calls[0].params[0];
assert.equal(cmd.keyword, 'stainless steel rings');
assert.equal(out.query_used, 'stainless steel rings');
// 给客户看的兜底搜索链接仍用原话
assert.match(out.search_url, /500%20stainless%20steel%20rings/);
});
test('search_products: falls back to site map when catalog returns empty', async () => {
const tools = createTools(baseCfg, {
rpc: fakeRpc(() => []),