#!/usr/bin/env node const fs = require('fs'); const os = require('os'); const path = require('path'); const { runTihaoSourcing } = require('../mcp/src/tools/tihao-brief-sourcing-run'); const { readFeedbackCsv } = require('./import-feedback-memory'); const { readMemory, applyFeedbackRecordsToMemory } = require('../mcp/src/features/tihao-sourcing/preference-memory'); async function main() { const out = fs.mkdtempSync(path.join(os.tmpdir(), 'tihao-feedback-roundtrip-')); const memoryPath = path.join(out, 'memory.json'); const feedbackPath = path.join(out, 'feedback.csv'); const blockedNames = [ '618母婴好物清单', '精致生活小鹿', '办公室健康零食测评', '真实家居清洁实验室' ]; fs.writeFileSync(feedbackPath, [ 'brief编号,平台,博主名称,主页链接,人工复核标签,反馈原因,归因类型,影响范围,是否升级为团队规则,下一轮动作', 'flower-skincare,小红书,618母婴好物清单,https://www.xiaohongshu.com/user/profile/xhs-dha-003,跑偏,护肤 Brief 不应优先推荐母婴清单号,需求解析,客户品牌级,否,剔除账号', 'flower-skincare,小红书,精致生活小鹿,https://www.xiaohongshu.com/user/profile/xhs-beauty-002,调性不符,封面下沉且主页质感不符,主页证据,团队规则,是,剔除账号', 'flower-skincare,小红书,办公室健康零食测评,https://www.xiaohongshu.com/user/profile/xhs-food-001,主页质感不符,封面下沉且主页质感不符,主页证据,团队规则,是,剔除账号', 'flower-skincare,小红书,真实家居清洁实验室,https://www.xiaohongshu.com/user/profile/xhs-home-001,参考账号不像,封面下沉且主页质感不符,主页证据,团队规则,是,剔除账号', 'flower-skincare,小红书,下沉硬广一号,https://www.xiaohongshu.com/user/profile/xhs-low-001,主页质感不符,封面下沉且主页质感不符,主页证据,团队规则,是,剔除账号', 'flower-skincare,小红书,下沉硬广二号,https://www.xiaohongshu.com/user/profile/xhs-low-002,主页质感不符,封面下沉且主页质感不符,主页证据,团队规则,是,剔除账号' ].join('\n'), 'utf8'); const memory = applyFeedbackRecordsToMemory(readMemory(memoryPath), readFeedbackCsv(feedbackPath)); fs.writeFileSync(memoryPath, JSON.stringify(memory, null, 2), 'utf8'); const result = await runTihaoSourcing({ collectionMode: 'sample', briefText: [ '客户名称:花漾肌研', '平台与人数:小红书 6 人,抖音 2 人', '粉丝量范围:2万-50万', '预算范围:3000-18000', '偏好真实体验、素人感、成分拆解、敏感肌修护,不要硬广。' ].join('\n'), memory: memoryPath, output: path.join(out, 'second-round') }); assert(result.status === 'ok', 'second round sourcing should return ok'); for (const name of blockedNames) { assert(memory.blockedCreators.includes(name), `${name} should be blocked in memory`); assert(!result.data.candidates.some(item => item.displayName === name), `${name} should not enter client-ready candidates`); } assert(memory.blockedStyles.some(item => item.includes('封面下沉且主页质感不符')), 'tone/homepage feedback should create blocked style memory'); assert(memory.teamRuleSuggestions.length >= 1, 'three repeated team-scope feedback rows should create team rule suggestion'); assert(memory.teamRuleSuggestions[0].count >= 3, 'team rule suggestion should require repeated evidence'); assert(result.data.criteria.memory.feedbackSummary.total === 6, 'second round should carry feedback summary'); console.log(`feedback roundtrip smoke ok: ${out}`); } function assert(condition, message) { if (!condition) throw new Error(message); } main().catch(error => { console.error(error && error.stack ? error.stack : String(error)); process.exit(1); });