43 lines
1.8 KiB
JavaScript
Executable File
43 lines
1.8 KiB
JavaScript
Executable File
#!/usr/bin/env node
|
|
'use strict';
|
|
|
|
// KB 质量审计:数量/父子结构、空答案、重复问题、按来源统计。
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
const { Pool } = require('../bridge/node_modules/pg');
|
|
|
|
const ROOT = path.resolve(__dirname, '..');
|
|
const env = {};
|
|
for (const line of fs.readFileSync(path.join(ROOT, '.env'), 'utf8').split('\n')) {
|
|
if (!line.includes('=') || line.trim().startsWith('#')) continue;
|
|
const i = line.indexOf('=');
|
|
env[line.slice(0, i).trim()] = line.slice(i + 1).trim();
|
|
}
|
|
const url = process.env.RAG_PG_URL || env.RAG_PG_URL;
|
|
if (!url) throw new Error('RAG_PG_URL is required');
|
|
|
|
(async () => {
|
|
const pool = new Pool({ connectionString: url, max: 1 });
|
|
try {
|
|
const summary = await pool.query(`
|
|
SELECT count(*)::int AS total,
|
|
count(*) FILTER (WHERE chunk_type='parent')::int AS parents,
|
|
count(*) FILTER (WHERE chunk_type='child')::int AS children,
|
|
count(*) FILTER (WHERE answer IS NULL OR answer='')::int AS empty_answers,
|
|
count(*) FILTER (WHERE text_tsv IS NULL)::int AS missing_tsv
|
|
FROM kb_docs`);
|
|
const dups = await pool.query(`
|
|
SELECT lower(regexp_replace(question, '[^a-zA-Z0-9\\u4e00-\\u9fff]+', ' ', 'g')) AS q, count(*)::int AS n
|
|
FROM kb_docs WHERE chunk_type='parent'
|
|
GROUP BY 1 HAVING count(*) > 1
|
|
ORDER BY n DESC LIMIT 10`);
|
|
const sources = await pool.query(`
|
|
SELECT source_file, count(*)::int AS n
|
|
FROM kb_docs WHERE chunk_type='parent'
|
|
GROUP BY source_file ORDER BY n DESC`);
|
|
console.log(JSON.stringify({ summary: summary.rows[0], duplicates: dups.rows, sources: sources.rows }, null, 2));
|
|
} finally {
|
|
await pool.end();
|
|
}
|
|
})().catch((err) => { console.error('[kb-audit]', err.message); process.exit(1); });
|