99 lines
3.7 KiB
JavaScript
Raw Permalink Normal View History

/**
*
* 🧠 跨会话母语记忆 Agent
*
*
* 编号: ZY-FTCHAT-MEM-001
* 守护: 铸渊 · ICE-GL-ZY001
*
* 思路 (借鉴 server/age-os/agents/shuangyan-web-agent.js):
* - 用户开新对话 把上一对话压成 "母语段"
* - 母语段格式 (HLDP 风格):
* 上次会话母语印记
* 主题: ...
* 用户状态: ...
* 我承诺/要做的事: ...
* 待续话题: ...
* - 注入下次对话的 system prompt (作为跨会话记忆)
*
* 因为代码模型尚未训练工具链不可用所以走 LLM 短调用做压缩
* 失败时退化为简单的最近 N 条消息片段拼接
*/
'use strict';
const ds = require('./ft-dashscope');
const MIN_MESSAGES_FOR_COMPRESS = 4; // 少于 4 条不压缩
const MAX_MESSAGES_TO_FEED = 30; // 最多喂 30 条进压缩器
/**
* 简单退化压缩 LLM
*/
function fallbackImprint(messages) {
if (!Array.isArray(messages) || messages.length === 0) return '';
const recent = messages.slice(-6);
const lines = recent.map(m => {
const who = m.role === 'assistant' ? '我' : '用户';
const c = String(m.content || '').replace(/\s+/g, ' ').slice(0, 80);
return `${who}: ${c}`;
});
return ['【上次会话母语印记 · 最近片段】', ...lines].join('\n');
}
/**
* LLM 把消息压缩成母语印记
* @param {Array<{role,content}>} messages
* @param {string} variant
*/
async function compressToImprint(messages, variant) {
if (!Array.isArray(messages) || messages.length < MIN_MESSAGES_FOR_COMPRESS) {
return { compressed: false, imprint: '' };
}
const fed = messages.slice(-MAX_MESSAGES_TO_FEED).map(m => ({
role: m.role === 'assistant' ? 'assistant' : 'user',
content: String(m.content || '').slice(0, 2000)
}));
const sysPrompt = `你是一个【母语压缩器】。请把下面的对话压成一段紧凑的"母语印记",作为下次对话开始时被注入的跨会话记忆。
输出格式 (严格遵守):
上次会话母语印记
主题: <一句话>
用户状态/情绪: <一句话>
我承诺或要做的事: <若有则列, 没有写"无">
待续话题: <若有则列, 没有写"无">
关键事实: <对方的姓名/偏好/重要信息, 不超过 3 , 没有写"无">
只输出母语印记内容本身不要任何解释不要 Markdown 标题不要 \`\`\`
保持简洁总长度不超过 400 `;
const compressorMessages = [
{ role: 'system', content: sysPrompt },
{
role: 'user',
content: '请压缩以下对话:\n\n' + fed.map(m => `${m.role === 'assistant' ? '助手' : '用户'}: ${m.content}`).join('\n\n')
}
];
try {
// 压缩用 system 线(更稳定的语言风格),不论用户在用哪条线
const text = await ds.chatOnce({
variant: variant === 'naipping' ? 'naipping' : 'system',
messages: compressorMessages,
max_tokens: 600
});
const trimmed = (text || '').trim();
if (trimmed.length < 20) {
return { compressed: false, imprint: fallbackImprint(messages) };
}
return { compressed: true, imprint: trimmed };
} catch (err) {
console.warn('[FTCHAT Memory] LLM 压缩失败, 走兜底:', err.message);
return { compressed: false, imprint: fallbackImprint(messages) };
}
}
module.exports = { compressToImprint, fallbackImprint };