mirror of
https://github.com/antvis/chart-visualization-skills.git
synced 2026-09-14 15:58:00 +08:00
b43ddb6543
* feat: skill use context service (#97) * feat: retrieve document as context with `zvec` (#87) * feat: 召回策略升级到 zvec * chore: 删除不必要的 createContext --------- Co-authored-by: 福晋 <liufu.lf@antgroup.com> * fix: 修复 playground 图表渲染异常 (#88) Co-authored-by: 福晋 <liufu.lf@antgroup.com> * refactor: antv skill refactor (#90) * refactor: antv skill refactor * chore: remove reference data from the eval process * chore: update api path * chore: remove mistakes content * chore: includeInfo → includeConstraints --------- Co-authored-by: 福晋 <liufu.lf@antgroup.com> * chore: skill content update (#93) * chore: skill content update * chore: update skill content --------- Co-authored-by: 福晋 <liufu.lf@antgroup.com> * chore: skill rename to doc (#92) * chore: skill rename to doc * chore: code opt --------- Co-authored-by: 福晋 <liufu.lf@antgroup.com> * refactor: use context for retrieval (#94) * refactor: use context for retrieval * chore: update dependence * chore: code opt * chore: update eval results * chore: cut down redundent code * chore: reduce external dependence * chore: update dependence version * fix: build * fix: utils * fix: test * chore: update node version --------- Co-authored-by: 福晋 <liufu.lf@antgroup.com> * refactor: 简化代码 (#96) * refactor: 简化代码 * chore: remove command * chore: update timeout * feat: add cli and format output * feat: add maxTokens * docs: add publish action * chore: update tc * feat: use exist zvec * chore: 0.1.4 --------- Co-authored-by: 逍为 <xiaowei.wzw@antgroup.com> * chore: merge master * chore: test should be first --------- Co-authored-by: Joel Alan <31396322+lxfu1@users.noreply.github.com> Co-authored-by: 福晋 <liufu.lf@antgroup.com> Co-authored-by: 逍为 <xiaowei.wzw@antgroup.com> * chore: add postinstall * chore: improvement of retrieval quality (#99) * chore: improvement of retrieval quality * chore: remove content from dist * chore: remove default ftsFields * chore: update version --------- Co-authored-by: 福晋 <liufu.lf@antgroup.com> * chore: update retrieve host (#100) Co-authored-by: 福晋 <liufu.lf@antgroup.com> --------- Co-authored-by: hustcc <i@hust.cc> Co-authored-by: 福晋 <liufu.lf@antgroup.com> Co-authored-by: 逍为 <xiaowei.wzw@antgroup.com>
126 lines
4.0 KiB
TypeScript
126 lines
4.0 KiB
TypeScript
#!/usr/bin/env tsx
|
||
/**
|
||
* AntV Skills 召回率评估
|
||
*
|
||
* 使用核心 retrieve() API(zvec hybrid search)评估召回率。
|
||
*/
|
||
|
||
import 'dotenv/config';
|
||
import fs from 'fs';
|
||
import path from 'path';
|
||
import { fileURLToPath } from 'url';
|
||
import { inferCategory } from './utils/category-inference.js';
|
||
|
||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||
|
||
// ── Retrieve via core (zvec hybrid) ─────────────────────────────────────────────
|
||
|
||
interface SkillEntry {
|
||
id: string;
|
||
title?: string;
|
||
category?: string;
|
||
}
|
||
|
||
async function retrieveSkillsViaCore(
|
||
query: string,
|
||
library: string,
|
||
topK = 5
|
||
): Promise<SkillEntry[]> {
|
||
try {
|
||
const mod = (await import('../src/api.js')) as {
|
||
retrieve: (
|
||
q: string,
|
||
opts: {
|
||
library?: string;
|
||
topK?: number;
|
||
content?: boolean;
|
||
}
|
||
) => Promise<Array<{ id: string; title: string; category: string }>>;
|
||
};
|
||
return await mod.retrieve(query, { library, topK, content: false });
|
||
} catch (err) {
|
||
console.warn(`检索失败 (${library}): ${(err as Error).message}`);
|
||
return [];
|
||
}
|
||
}
|
||
|
||
// ── 评估函数 ───────────────────────────────────────────────────────────────────
|
||
|
||
async function evaluateRecall() {
|
||
const datasetPath = path.join(__dirname, 'data', 'g2-dataset-174.json');
|
||
const dataset: Array<{ id: string; description: string }> = JSON.parse(
|
||
fs.readFileSync(datasetPath, 'utf-8')
|
||
);
|
||
|
||
console.log('\n' + '='.repeat(60));
|
||
console.log('📊 AntV Skills 召回率评估 (zvec hybrid)');
|
||
console.log('='.repeat(60));
|
||
console.log(`📋 测试用例数: ${dataset.length}`);
|
||
|
||
const categoryStats: Record<string, { total: number; hit: number }> = {};
|
||
let totalHit = 0;
|
||
let totalWithResults = 0;
|
||
|
||
for (const { description } of dataset) {
|
||
const library =
|
||
description.includes('X6') || description.includes('@antv/x6')
|
||
? 'x6'
|
||
: description.includes('G6') || description.includes('图分析')
|
||
? 'g6'
|
||
: 'g2';
|
||
|
||
const results = await retrieveSkillsViaCore(description, library, 5);
|
||
if (results.length === 0) continue;
|
||
|
||
const expectedCategory = inferCategory(description);
|
||
const hit = results.some((s) => s.category === expectedCategory);
|
||
|
||
if (!categoryStats[expectedCategory]) {
|
||
categoryStats[expectedCategory] = { total: 0, hit: 0 };
|
||
}
|
||
categoryStats[expectedCategory].total++;
|
||
if (hit) {
|
||
categoryStats[expectedCategory].hit++;
|
||
totalHit++;
|
||
}
|
||
totalWithResults++;
|
||
}
|
||
|
||
console.log('\n📈 总体结果');
|
||
console.log('─'.repeat(40));
|
||
console.log(`有检索结果的用例: ${totalWithResults}/${dataset.length}`);
|
||
console.log(
|
||
`类别命中率: ${totalHit}/${totalWithResults} (${((totalHit / totalWithResults) * 100).toFixed(1)}%)`
|
||
);
|
||
|
||
console.log('\n📊 分类别统计');
|
||
console.log('─'.repeat(40));
|
||
|
||
for (const [category, stats] of Object.entries(categoryStats).sort(
|
||
(a, b) => b[1].hit - a[1].hit
|
||
)) {
|
||
const hitRate = (stats.hit / stats.total) * 100;
|
||
console.log(
|
||
`${category.padEnd(20)} 命中率: ${hitRate.toFixed(1).padStart(5)}% (${stats.hit}/${stats.total})`
|
||
);
|
||
}
|
||
|
||
console.log('\n📝 检索示例');
|
||
console.log('─'.repeat(40));
|
||
for (const { id, description } of dataset.slice(0, 5)) {
|
||
const library =
|
||
description.includes('X6') || description.includes('@antv/x6')
|
||
? 'x6'
|
||
: description.includes('G6') || description.includes('图分析')
|
||
? 'g6'
|
||
: 'g2';
|
||
const results = await retrieveSkillsViaCore(description, library, 3);
|
||
console.log(`\n[${id}] ${description.substring(0, 50)}...`);
|
||
console.log(` 检索结果: ${results.map((s) => s.id).join(', ')}`);
|
||
}
|
||
|
||
console.log('\n' + '='.repeat(60) + '\n');
|
||
}
|
||
|
||
evaluateRecall();
|