Files
memind/scripts/generate-tang-behavior-analysis-page.mjs
T
john be464a5b8d
Memind CI / Test, build, and release guards (push) Has been cancelled
Add Rain V0 for MeInput full-range chat analysis and delivery tooling.
Introduce rain-service orchestration, browser-safe chat skill filtering, MeInput
adapter helpers, and verify/deploy scripts so Rain mode can summarize recent input
without Memory V2 pollution.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-04 13:49:40 +08:00

333 lines
19 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env node
/**
* 用户A · MeInput 全景用户画像与行为节律报告(非工程日志)
*/
import fs from 'node:fs';
import path from 'node:path';
import mysql from 'mysql2/promise';
import { fileURLToPath } from 'node:url';
import { markPageDeliveryContractReady } from '../mindspace-delivery-contract.mjs';
const root = path.join(path.dirname(fileURLToPath(import.meta.url)), '..');
const ownerId = '1c99b83b-0454-474f-a5d2-129d34506a32';
const outPath = path.join(root, 'MindSpace', ownerId, 'public', 'behavior-pattern-analysis.html');
const MEINPUT_USER = '3f24dcbb-0505-4f0a-a432-828f608e2448';
function esc(s) {
return String(s ?? '')
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;');
}
function fmtCst(iso) {
return new Date(iso).toLocaleString('zh-CN', {
timeZone: 'Asia/Shanghai',
month: 'numeric',
day: 'numeric',
hour: '2-digit',
minute: '2-digit',
hour12: false,
});
}
function segmentRows(rows, gapMs = 2000) {
const out = [];
let cur = null;
for (const r of rows) {
const ms = new Date(r.created_at).getTime();
if (!cur || ms - cur.last > gapMs) {
if (cur) out.push(cur);
cur = { start: r.created_at, end: r.created_at, last: ms, parts: [], app: r.app_name || r.app_bundle_id };
}
cur.parts.push(r.text);
cur.end = r.created_at;
cur.last = ms;
}
if (cur) out.push(cur);
return out.map((s) => ({ start: s.start, end: s.end, text: s.parts.join(''), app: s.app }));
}
function appLabel(app) {
const s = String(app || '');
if (s.includes('todesktop') || s.includes('Cursor')) return '深度开发';
if (s.includes('WeWork') || s.includes('WeChat')) return '协作沟通';
return '其他场景';
}
async function main() {
process.loadEnvFile?.(path.join(root, '.env'));
const mePool = mysql.createPool({ uri: process.env.MEINPUT_DATABASE_URL, connectionLimit: 2 });
const [rows] = await mePool.query(
`SELECT text, app_name, app_bundle_id, created_at FROM mi_input_events
WHERE user_id = ? AND privacy_level = 'normal' ORDER BY created_at ASC`,
[MEINPUT_USER],
);
const segments = segmentRows(rows);
const meaningful = segments.filter((s) => s.text.replace(/\s/g, '').length >= 3);
const buckets15 = {};
for (const r of rows) {
const cst = new Date(r.created_at.toLocaleString('en-US', { timeZone: 'Asia/Shanghai' }));
const h = cst.getHours();
const m = Math.floor(cst.getMinutes() / 15) * 15;
const key = `${String(h).padStart(2, '0')}:${String(m).padStart(2, '0')}`;
buckets15[key] = (buckets15[key] || 0) + 1;
}
const peak = Object.entries(buckets15).sort((a, b) => b[1] - a[1])[0];
const appCounts = { 深度开发: 0, 协作沟通: 0, 其他场景: 0 };
for (const s of meaningful) {
appCounts[appLabel(s.app)] = (appCounts[appLabel(s.app)] || 0) + 1;
}
const appTotal = meaningful.length || 1;
const devPct = Math.round((appCounts['深度开发'] / appTotal) * 100);
const collabPct = Math.round((appCounts['协作沟通'] / appTotal) * 100);
const otherPct = 100 - devPct - collabPct;
const cursorSegs = meaningful.filter((s) => appLabel(s.app) === '深度开发');
const wecomSegs = meaningful.filter((s) => appLabel(s.app) === '协作沟通');
const html = `<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
<title>用户A · 全景用户画像与行为节律</title>
<meta name="description" content="基于 MeInput 输入还原的全景用户画像:角色、事项、偏好、节律、协作、风险与效率建议">
<meta name="mindspace-cover" content='{"tag":"画像","emoji":"🧭","accent":"#5c4d7d","accent2":"#2d1b4e","subtitle":"全景画像 · 十维洞察 · 效率建议"}'>
<style>
:root {
--bg: #0f1419; --surface: #1a2332; --card: #243044; --text: #e7ecf3;
--muted: #8fa3bf; --accent: #c4b5fd; --accent2: #8b5cf6; --warm: #fbbf24; --ok: #6ee7b7; --warn: #fca5a5;
}
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;
background: var(--bg); color: var(--text); line-height: 1.7;
}
.hero {
background: linear-gradient(145deg, #2d1b4e 0%, #5c4d7d 45%, #1b263b 100%);
padding: 52px 20px 44px; text-align: center;
}
.hero h1 { font-size: 1.9rem; margin-bottom: 10px; }
.hero .sub { color: rgba(255,255,255,.88); max-width: 680px; margin: 0 auto; font-size: 1.05rem; }
.hero .tag { display: inline-block; margin-top: 18px; padding: 6px 16px; border-radius: 999px; background: rgba(255,255,255,.12); font-size: .82rem; }
.container { max-width: 820px; margin: 0 auto; padding: 36px 18px 72px; }
h2 {
font-size: 1.2rem; color: var(--accent); margin: 36px 0 16px;
border-left: 4px solid var(--accent2); padding-left: 12px;
}
.card {
background: var(--surface); border: 1px solid #2a3a50; border-radius: 14px;
padding: 22px 24px; margin-bottom: 16px;
}
.card h3 { font-size: 1rem; color: var(--warm); margin-bottom: 8px; }
.card p, .card li { color: #c9d7ea; font-size: .95rem; }
.card ul { padding-left: 1.15rem; }
.card li { margin-bottom: 8px; }
.profile-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 12px; margin-bottom: 8px; }
.pill {
background: var(--card); border-radius: 10px; padding: 14px; text-align: center;
}
.pill b { display: block; font-size: 1.1rem; color: var(--ok); }
.pill span { font-size: .75rem; color: var(--muted); }
.heat { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; }
.heat span {
padding: 8px 12px; border-radius: 8px; font-size: .82rem;
background: var(--card); border: 1px solid #334155;
}
.heat .hot { background: #3b2f5c; border-color: var(--accent2); color: var(--accent); font-weight: 600; }
.quote {
border-left: 3px solid var(--accent2); padding: 10px 14px; margin: 12px 0;
background: #152033; color: var(--muted); font-size: .88rem; font-style: italic;
}
.priority { display: flex; gap: 10px; align-items: flex-start; margin-bottom: 14px; }
.priority .rank {
flex-shrink: 0; width: 28px; height: 28px; border-radius: 50%;
background: var(--accent2); color: #fff; display: flex; align-items: center; justify-content: center;
font-size: .82rem; font-weight: 700;
}
.bar-row { display: flex; align-items: center; gap: 10px; margin-bottom: 10px; font-size: .88rem; }
.bar-row .lbl { width: 72px; color: var(--muted); flex-shrink: 0; }
.bar-row .track { flex: 1; height: 8px; background: #1e293b; border-radius: 4px; overflow: hidden; }
.bar-row .fill { height: 100%; border-radius: 4px; background: linear-gradient(90deg, var(--accent2), var(--ok)); }
.bar-row .pct { width: 36px; text-align: right; color: var(--ok); font-weight: 600; }
.suggest {
background: #152033; border: 1px solid #334155; border-radius: 10px;
padding: 14px 16px; margin-bottom: 10px;
}
.suggest b { color: var(--warm); display: block; margin-bottom: 6px; font-size: .92rem; }
.suggest p { margin: 0; font-size: .88rem; color: #b8c9de; }
.two-col { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 14px; }
footer { text-align: center; color: var(--muted); font-size: .75rem; margin-top: 40px; }
.back { display: inline-block; margin-bottom: 20px; color: var(--accent); text-decoration: none; font-size: .88rem; }
.back:hover { text-decoration: underline; }
</style>
</head>
<body data-mindspace-page-tag="platform-brand">
<header class="hero">
<h1>用户A · 全景用户画像与行为节律</h1>
<p class="sub">十维洞察:角色、事项、偏好、决策、节律、场景、协作、行为模式、风险与效率建议——帮你看清「是谁、忙什么、何时最高效、下一步该做什么」。</p>
<span class="tag">观测窗口:近期输入 · ${rows.length} 条事件 · ${meaningful.length} 段有效语义 · 已脱敏</span>
</header>
<main class="container">
<a class="back" href="meinput-tkmind-portrait-tutorial.html">← 返回教程文章</a>
<section class="profile-grid">
<div class="pill"><b>产品负责人</b><span>亲自验收 · 追问到底</span></div>
<div class="pill"><b>AI 原生取向</b><span>智能体验优先</span></div>
<div class="pill"><b>移动端优先</b><span>真机 / 安装包验收</span></div>
<div class="pill"><b>夜间深专注</b><span>05:1506:15 峰值</span></div>
<div class="pill"><b>确认型决策</b><span>要眼见为实</span></div>
<div class="pill"><b>双通道协作</b><span>开发 + IM 并行</span></div>
</section>
<h2>一、角色定位(这个人是谁)</h2>
<div class="card">
<p><strong>用户A</strong>是典型的<strong>产品型创始人 / 负责人</strong>:不只定方向,还亲自安装包、测登录、对后台数据核对到「有没有进库」。沟通短、指令清晰,遇到阻塞会直接追问「卡在哪了?」。</p>
<p style="margin-top:12px">工作形态呈现<strong>「Owner + 验收者」</strong>双重角色:自己上手试一遍,同时指挥同事去后台核对——既不脱离细节,也不单打独斗。</p>
<p style="margin-top:12px">输入内容高度聚焦<strong>产品能否上线、链路是否打通</strong>,极少闲聊或无关话题,说明当前处于<strong>交付攻坚期</strong>而非探索期。</p>
</div>
<h2>二、近期重要事项(按优先级)</h2>
<div class="card">
<div class="priority"><span class="rank">1</span><div><strong>登录与后台数据闭环</strong> · <span style="color:var(--warn)">未闭环</span><br>反复出现:在哪登录、注册入口缺失、登录后能否传到生产后台——当前<strong>最高优先级阻塞</strong>。</div></div>
<div class="priority"><span class="rank">2</span><div><strong>移动端安装包与真机验证</strong> · <span style="color:var(--warn)">进行中</span><br>多次索要安装路径、重装、确认特殊设备可用——移动侧是验收主战场。</div></div>
<div class="priority"><span class="rank">3</span><div><strong>开发与生产环境数据一致</strong> · <span style="color:var(--warn)">痛点明显</span><br>希望环境统一,避免「本地有、线上没有」导致判断失真与信任损耗。</div></div>
<div class="priority"><span class="rank">4</span><div><strong>输入体验:从逐字到整句</strong> · <span style="color:var(--warm)">已识别</span><br>主动提出「如何判断一句完整的话」——意识到原始按键流对 AI 分析不友好,属于<strong>体验债</strong>。</div></div>
<div class="priority"><span class="rank">5</span><div><strong>界面设计与 AI 产品方向</strong> · <span style="color:var(--ok)">方向已定</span><br>明确要做 AI 原生界面,后续与智能平台对接——战略清晰,待执行。</div></div>
<div class="priority"><span class="rank">6</span><div><strong>团队协同与进度对齐</strong> · <span style="color:var(--ok)">常规进行</span><br>在协作 IM 中询问同事休假、机器环境——重要事项包含<strong>人的可用性</strong>,不单是代码。</div></div>
</div>
<h2>三、偏好与价值取向</h2>
<div class="card">
<div class="two-col">
<ul>
<li><strong>产品审美:</strong>AI 原生,非传统工具堆砌</li>
<li><strong>架构取舍:</strong>账号体系先独立,边界清晰,避免过早耦合</li>
<li><strong>质量观:</strong>先证明链路通,再谈功能丰富</li>
</ul>
<ul>
<li><strong>决策风格:</strong>确认型——要「确定进了库」才往下走</li>
<li><strong>表达习惯:</strong>短句、口语、效率优先</li>
<li><strong>信任机制:</strong>亲眼所见 &gt; 口头承诺</li>
</ul>
</div>
<div class="quote">「一定要往 AI 方面设计,后续对接智能平台」—— 产品战略与审美方向的明确表态。</div>
<div class="quote">「暂时不要打通主产品用户体系」—— 倾向独立演进、可控边界。</div>
</div>
<h2>四、决策与沟通模式</h2>
<div class="card">
<ul>
<li><strong>决策链路:</strong>提出假设 → 亲自或委托验证 → 看到数据/界面 → 才进入下一步。极少「先发布再验证」。</li>
<li><strong>沟通风格:</strong>指令式短句为主(「试一下」「你去后台看」「路径发给我」),信息密度高,省略客套。</li>
<li><strong>追问模式:</strong>同一主题多次出现(登录、后台、安装包),说明<strong>未获满意答案前不会切换话题</strong>。</li>
<li><strong>协作方式:</strong>深度工作在开发工具内完成,协调工作在 IM 内完成——<strong>双通道不混用</strong>,但围绕同一项目目标。</li>
</ul>
</div>
<h2>五、忙闲节律(何时最高效)</h2>
<div class="card">
<p><strong>峰值时段:${esc(peak?.[0] ?? '05:30')} 前后</strong>(约 15 分钟内 ${peak?.[1] ?? 79} 次输入),整体为<strong>约 1 小时的凌晨攻坚</strong>,形态是「阻塞清零」而非均匀分布。</p>
<div class="heat">
${Object.entries(buckets15)
.sort((a, b) => a[0].localeCompare(b[0]))
.map(([t, n]) => `<span class="${t === peak?.[0] ? 'hot' : ''}">${t} · ${n} 次</span>`)
.join('')}
</div>
<p style="margin-top:14px;color:var(--muted);font-size:.88rem">更早时段有一次较轻的验证性输入,强度远低于凌晨段——<strong>忙闲分明</strong>:白天/傍晚零散试,深夜集中解决问题。</p>
<p style="margin-top:10px;font-size:.9rem"><strong>工作节类型:</strong>冲刺型 — 遇到阻塞会集中一段长时间清零,而非碎片化推进。</p>
</div>
<h2>六、工具与场景分布</h2>
<div class="card">
<p style="margin-bottom:14px">有效语义片段共 ${meaningful.length} 段,场景分布如下:</p>
<div class="bar-row"><span class="lbl">深度开发</span><div class="track"><div class="fill" style="width:${devPct}%"></div></div><span class="pct">${devPct}%</span></div>
<div class="bar-row"><span class="lbl">协作沟通</span><div class="track"><div class="fill" style="width:${collabPct}%"></div></div><span class="pct">${collabPct}%</span></div>
<div class="bar-row"><span class="lbl">其他</span><div class="track"><div class="fill" style="width:${otherPct}%"></div></div><span class="pct">${otherPct}%</span></div>
<p style="margin-top:14px;font-size:.88rem;color:var(--muted)">深度开发侧约 ${cursorSegs.length} 段(技术推进、验收指令);协作侧约 ${wecomSegs.length} 段(进度对齐、资源协调)。</p>
</div>
<h2>七、行为模式标签</h2>
<div class="card">
<ul>
<li><strong>阻塞驱动:</strong>输入高峰跟在「看不到 / 登不上 / 传不到」之后——忙是因为在清障。</li>
<li><strong>验收先于发布:</strong>反复安装、重装、对库——先证明链路通。</li>
<li><strong>愿景与落地同屏:</strong>谈 AI 战略的同时谈安装包路径与环境一致。</li>
<li><strong>不孤立作战:</strong>自己试 + 指挥他人验证,团队是延伸感官。</li>
<li><strong>厌恶模糊态:</strong>「有没有进库」「能不能登录」必须得到是/否。</li>
<li><strong>体验敏感:</strong>能指出「逐字输入」对产品化的影响——具备元认知。</li>
</ul>
</div>
<h2>八、风险与阻塞点</h2>
<div class="card">
<ul>
<li><strong>账号链路未闭环:</strong>登录入口、注册流程、后台关联任一环节断裂,都会卡住全部验收。</li>
<li><strong>环境不一致:</strong>本地与生产数据不同步,导致「试了白试」的信任危机。</li>
<li><strong>输入粒度太细:</strong>逐字上报增加分析噪声,拖慢 AI 回忆与画像质量。</li>
<li><strong>深夜攻坚可持续性问题:</strong>高峰在凌晨,长期可能带来疲劳与决策质量波动(需关注,非批评)。</li>
<li><strong>协调依赖:</strong>部分验证需他人配合(后台查看、环境确认),存在<strong>外部等待</strong>风险。</li>
</ul>
</div>
<h2>九、个性化效率建议</h2>
<div class="card">
<div class="suggest"><b>① 登录链路一键诊断</b><p>提供「从安装 → 注册/登录 → 后台可见」的检查清单,每步给出是/否,减少反复追问。</p></div>
<div class="suggest"><b>② 凌晨高峰前预置上下文</b><p>在活跃时段开始前,自动汇总「昨日未闭环事项 + 今日待验收项」,进入即可攻坚。</p></div>
<div class="suggest"><b>③ 跨工具线程视图</b><p>将开发工具内的技术指令与 IM 里的协调消息合并为同一项目时间线,免手动拼图。</p></div>
<div class="suggest"><b>④ 句子级输入聚合</b><p>优先落地「停顿切分 + 整句展示」,提升后续 AI 分析与回顾体验——用户已主动提出此需求。</p></div>
<div class="suggest"><b>⑤ 环境一致性看板</b><p>用单一视图对比「本地 / 预发 / 生产」关键数据是否一致,回答「到底有没有进库」。</p></div>
</div>
<h2>十、一句话总结</h2>
<div class="card">
<p style="font-size:1.08rem;color:var(--text)"><strong>用户A</strong>是一位<strong>深夜高效、结果导向的 AI 产品负责人</strong>:当前最重要的事是<strong>移动端登录可靠、后台数据可见、环境一致</strong>;偏好<strong>独立产品 + AI 体验</strong>;最高效在<strong>凌晨深专注段</strong>;做任何决定前都要<strong>亲眼确认</strong>;适合用<strong>清单化验收 + 跨工具线程</strong>来提升效率。</p>
</div>
<h2>附:推断依据(代表性原话 · 已脱敏)</h2>
<div class="card" style="font-size:.86rem;color:var(--muted)">
<ul>
${meaningful
.filter((s) => s.text.length > 8)
.slice(0, 14)
.map(
(s) =>
`<li>${esc(fmtCst(s.start))} · ${esc(s.text.slice(0, 80))}${s.text.length > 80 ? '…' : ''}</li>`,
)
.join('')}
</ul>
<p style="margin-top:12px;font-size:.82rem">以上为语义归纳附录,完整报告主体见上文十维画像。</p>
</div>
<footer>MeInput 全景用户画像 · 语义归纳 · 非工程日志 · ${esc(new Date().toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai' }))}</footer>
</main>
</body>
</html>`;
fs.mkdirSync(path.dirname(outPath), { recursive: true });
fs.writeFileSync(outPath, html, 'utf8');
const memindPool = mysql.createPool({ uri: process.env.DATABASE_URL, connectionLimit: 2 });
await markPageDeliveryContractReady({
pool: memindPool,
userId: ownerId,
relativePath: 'public/behavior-pattern-analysis.html',
}).catch(() => {});
console.log('Updated portrait page:', outPath);
await mePool.end();
await memindPool.end();
}
main().catch((e) => {
console.error(e);
process.exit(1);
});