Files

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); });