TripSense 行知 · 周边出行顾问:首版
零依赖 Node 服务,三步流程(选出发地 → 分析候选 → 生成方案 + 路线图 + 住宿参考)。 - 候选发现:高德 place/text 类型词跨城检索 + 内置景区/城市/小众库 + Photon 兜底 - 区县级「排除同城」判定(adname),过滤发生在轮转取样之前 - 路线图画的是 AI 方案里实际安排的每个地点,按正文顺序编号 + 虚线动线 + 真实高德底图 - 住宿:RollingGo 只读 CLI 取实时报价,无价格源时如实标注不编造 - 服务端内存缓存(指纹 + TTL + LRU),数据源层与 HTTP 层分别缓存 - 上下文瘦身:逐日天气只给 rank1,紧凑 JSON,实测约 1335 token
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
'use strict';
|
||||
/* 对比两次 analyze 请求体,定位缓存指纹不稳定的原因 */
|
||||
const BASE = process.env.BASE || 'http://127.0.0.1:8788';
|
||||
const AMAP = process.env.AMAP_KEY || '';
|
||||
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
||||
|
||||
class CDP {
|
||||
constructor(ws) { this.ws = ws; this.id = 0; this.waits = new Map(); }
|
||||
static async connect(url) {
|
||||
const ws = new WebSocket(url);
|
||||
await new Promise((res, rej) => { ws.onopen = res; ws.onerror = rej; });
|
||||
const c = new CDP(ws);
|
||||
ws.onmessage = (e) => {
|
||||
const m = JSON.parse(e.data);
|
||||
if (m.id && c.waits.has(m.id)) { c.waits.get(m.id)(m); c.waits.delete(m.id); }
|
||||
};
|
||||
return c;
|
||||
}
|
||||
send(method, params = {}, sessionId) {
|
||||
const id = ++this.id;
|
||||
return new Promise((res) => { this.waits.set(id, res); this.ws.send(JSON.stringify({ id, method, params, sessionId })); });
|
||||
}
|
||||
}
|
||||
|
||||
(async () => {
|
||||
const { webSocketDebuggerUrl } = await (await fetch('http://127.0.0.1:9333/json/version')).json();
|
||||
const browser = await CDP.connect(webSocketDebuggerUrl);
|
||||
const { result: { targetId } } = await browser.send('Target.createTarget', { url: 'about:blank' });
|
||||
const { result: { sessionId } } = await browser.send('Target.attachToTarget', { targetId, flatten: true });
|
||||
const S = (m, p) => browser.send(m, p, sessionId);
|
||||
await S('Page.enable'); await S('Runtime.enable');
|
||||
const evalJs = async (expr) => {
|
||||
const r = await S('Runtime.evaluate', { expression: expr, awaitPromise: true, returnByValue: true });
|
||||
if (r.result?.exceptionDetails) throw new Error(JSON.stringify(r.result.exceptionDetails).slice(0, 400));
|
||||
return r.result?.result?.value;
|
||||
};
|
||||
|
||||
await S('Page.navigate', { url: BASE });
|
||||
await sleep(2500);
|
||||
if (AMAP) {
|
||||
await evalJs(`(() => { const raw = JSON.parse(localStorage.getItem('tripsense.ai') || '{}'); raw.amapKey = ${JSON.stringify(AMAP)}; localStorage.setItem('tripsense.ai', JSON.stringify(raw)); return true; })()`);
|
||||
await S('Page.reload');
|
||||
await sleep(2500);
|
||||
}
|
||||
// 拦截 fetch,记录 /api/analyze 的请求体
|
||||
await evalJs(`(() => {
|
||||
window.__bodies = [];
|
||||
const of = window.fetch;
|
||||
window.fetch = async function (u, o) {
|
||||
if (String(u).includes('/api/analyze') && o && o.body) window.__bodies.push(o.body);
|
||||
return of.apply(this, arguments);
|
||||
};
|
||||
return true;
|
||||
})()`);
|
||||
|
||||
await evalJs(`(() => { const el = document.querySelector('#originInput'); el.value = '柳州'; el.dispatchEvent(new Event('input', { bubbles: true })); return true; })()`);
|
||||
for (let i = 0; i < 20; i++) {
|
||||
await sleep(1000);
|
||||
const ok = await evalJs(`(() => { const it = document.querySelector('#suggestBox .suggest-item'); if (it && !document.querySelector('#suggestBox').classList.contains('hidden')) { it.click(); return true; } return false; })()`);
|
||||
if (ok) break;
|
||||
}
|
||||
|
||||
for (let i = 1; i <= 2; i++) {
|
||||
await evalJs(`(() => { const b = document.querySelector('#analyzeBtn'); if (b) b.click(); return true; })()`);
|
||||
for (let k = 0; k < 40; k++) {
|
||||
await sleep(500);
|
||||
const n = await evalJs(`document.querySelectorAll('.dest-card').length`);
|
||||
if (n) break;
|
||||
}
|
||||
await sleep(1500);
|
||||
console.log(`run ${i} done, cards =`, await evalJs(`document.querySelectorAll('.dest-card').length`));
|
||||
}
|
||||
|
||||
const bodies = await evalJs(`window.__bodies`);
|
||||
console.log('\n=== captured analyze bodies ===');
|
||||
(bodies || []).forEach((b, i) => console.log(`[${i}]`, b));
|
||||
if ((bodies || []).length >= 2) {
|
||||
console.log('\nidentical =', bodies[0] === bodies[1]);
|
||||
}
|
||||
browser.ws.close();
|
||||
process.exit(0);
|
||||
})().catch((e) => { console.error('FAIL', e.message); process.exit(1); });
|
||||
Reference in New Issue
Block a user