Files
memind/rain-service/llm-analysis.mjs
T
john d2d463b1db
Memind CI / Test, build, and release guards (push) Successful in 5m5s
feat(rain): add date preset picker and reduce time clarification prompts.
Default to last 3 days in UI and backend, only ask when time is truly ambiguous, and treat「做成页面」as HTML report pages without follow-up questions.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-07 16:54:11 +08:00

123 lines
5.0 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.
function stripRainSkillPrefix(text) {
let next = String(text ?? '').trim();
next = next.replace(/^【Rain[^】]*】\s*/u, '');
next = next.replace(/^请使用\s+rain\s+技能[:]\s*/iu, '');
if (/^请描述要分析的时间/u.test(next) || /^在下方选择时间范围/u.test(next)) {
const marker = '我的问题是:';
const idx = next.indexOf(marker);
if (idx >= 0) next = next.slice(idx + marker.length);
}
return next.trim();
}
function parseRainLlmJson(raw) {
const text = String(raw ?? '').trim();
const fenced = text.match(/```(?:json)?\s*([\s\S]*?)```/i);
const candidate = fenced?.[1]?.trim() || text;
try {
return JSON.parse(candidate);
} catch {
return null;
}
}
/**
* @param {{ llmProviderService: object, userQuery: string, meinputBlock: string, timeRangeLabel: string, recordCount: number }} input
*/
export async function runRainLlmAnalysis(input) {
const userQuery = stripRainSkillPrefix(input.userQuery);
const system = [
'你是 TKMind Rain 分析层。只能依据【MeInput 原始输入】块中的内容做归纳,禁止引用或编造长期记忆、聊天历史、日程等外部信息。',
'输出必须是单个 JSON 对象,不要 markdown,不要代码围栏,字段如下:',
'{"needs_clarification":boolean,"clarification_question":string|null,"user_goal":string,"meinput_analysis":string,"suggested_next_steps":string[],"user_reply":string}',
'- needs_clarification=true 时:clarification_question 必填,user_reply 用自然语言向用户追问;meinput_analysis 可为空。',
'- needs_clarification=false 时:meinput_analysis 按时间线归纳用户在各 App 的输入活动;user_reply 是可直接展示给用户的中文回复(含区间说明);suggested_next_steps 供下游 Agent 参考(如生成报告页、继续追问)。',
'- 时间区间已在输入中给定,禁止追问「这几天指哪几天」或让用户再次确认区间;若查询范围内实际有记录的时段更短,在 user_reply 中说明实际覆盖时段即可。',
'- 用户提到「做成页面」「生成报告」时,默认理解为 MindSpace HTML 可视化报告页;suggested_next_steps 应包含生成 HTML 报告页,禁止追问是要卡片还是页面。',
'- needs_clarification 仅用于用户诉求本身完全无法理解(与时间区间、页面形式无关)。',
'- 不要把内部排序分数、source 字段名暴露给用户。',
].join('\n');
const user = [
`时间区间:${input.timeRangeLabel}`,
`记录条数:${input.recordCount}`,
'',
input.meinputBlock,
'',
`用户诉求:${userQuery || '请总结我在上述区间的输入活动'}`,
].join('\n');
const completion = await input.llmProviderService.createChatCompletion({
messages: [
{ role: 'system', content: system },
{ role: 'user', content: user },
],
});
if (!completion?.ok) {
const err = new Error(completion?.message ?? 'Rain LLM 分析失败');
err.code = 'RAIN_LLM_FAILED';
throw err;
}
const parsed = parseRainLlmJson(completion.reply);
if (!parsed || typeof parsed !== 'object') {
return {
needs_clarification: false,
clarification_question: null,
user_goal: userQuery || '回顾 MeInput 输入',
meinput_analysis: String(completion.reply ?? '').trim(),
suggested_next_steps: [],
user_reply: String(completion.reply ?? '').trim(),
raw: completion.reply,
};
}
return {
needs_clarification: Boolean(parsed.needs_clarification),
clarification_question: parsed.clarification_question ?? null,
user_goal: String(parsed.user_goal ?? userQuery ?? '').trim(),
meinput_analysis: String(parsed.meinput_analysis ?? '').trim(),
suggested_next_steps: Array.isArray(parsed.suggested_next_steps)
? parsed.suggested_next_steps.map((s) => String(s).trim()).filter(Boolean)
: [],
user_reply: String(parsed.user_reply ?? '').trim(),
raw: completion.reply,
};
}
export function buildRainGooseHandoffText({
userQuery,
timeRangeLabel,
recordCount,
analysis,
userGoal,
suggestedNextSteps = [],
}) {
const steps =
suggestedNextSteps.length > 0
? suggestedNextSteps.map((s) => `- ${s}`).join('\n')
: '- (无明确工具动作,先给用户文字总结)';
return [
'[Rain · MeInput 分析简报]',
'以下简报由 Rain 分析层基于 MeInput 全量原始输入生成。请据此决定如何回复用户、是否调用工具或 skill;不要重复询问时间区间。',
'若用户诉求含「做成页面」或「生成报告」,直接生成 MindSpace HTML 可视化报告页,勿再追问形式。',
'',
`时间区间:${timeRangeLabel}`,
`原始记录条数:${recordCount}`,
`用户诉求:${userGoal || stripRainSkillPrefix(userQuery)}`,
'',
'【分析归纳】',
analysis || '(无)',
'',
'【建议下一步】',
steps,
'',
'【用户原始问题】',
stripRainSkillPrefix(userQuery),
].join('\n');
}
export { stripRainSkillPrefix };