跳到主要内容

DeepSeek Harness 本地聊天记忆:按来源保存聊天原文,关键词 + 向量检索,可选大模型重排与检索摘要。源码公开,仅限非商用

浏览文件
main

分支

corpus.js

97 行 · 8.9 KB历史下载
1/**
2 * Retrieval evaluation corpus: QQ-style private and group chats a companion would see.
3 * Synthetic and deterministic. `facts` carry the answers; `filler` is everyday chatter that
4 * shares common words with the queries, so ranking has to work for its place.
5 *
6 * Query kinds:
7 * exact the query reuses words from the record
8 * paraphrase different wording, still sharing some characters
9 * context the record that matches the query lacks the answer; it is in the next turn
10 * update a fact changed; the newer record must outrank the older one
11 * noisy the question is buried in a long bridge-style prompt
12 * semantic no shared characters at all; lexical retrieval cannot find it (tracked, not gated)
13 */
14const DAY = 86400000;
15const T0 = Date.UTC(2026, 8, 1, 2, 0, 0);
16const at = (day, minute = 0) => T0 + day * DAY + minute * 60000;
17
18export const OWNER = 'owner';
19export const GROUP = 'group:73120001';
20export const GROUP2 = 'group:73120002';
21export const OTHER = 'private:20002';
22
23export const facts = [
24 { key: 'cat', scope: OWNER, time: at(1), content: '用户:我家那只橘猫叫团子,三岁了,特别能吃\n助手:团子听起来好可爱!' },
25 { key: 'birthday', scope: OWNER, time: at(2), content: '用户:跟你说哦,我生日是3月15号\n助手:记住啦,3月15号!' },
26 { key: 'allergy', scope: GROUP, time: at(3), content: '用户:【新消息】\n阿杰:我对花生过敏,上次吃了一口宫保鸡丁直接进医院\n助手:天哪,那以后点菜得注意' },
27 { key: 'trip-ask', scope: OWNER, time: at(4, 0), content: '用户:这周末去哪玩比较好\n助手:天气不错的话,海边或者爬山都可以' },
28 { key: 'trip-decide', scope: OWNER, time: at(4, 3), content: '用户:那就海边吧,周六早上八点在地铁站集合\n助手:好嘞,到时候提醒你带防晒' },
29 { key: 'home-old', scope: OWNER, time: at(5), content: '用户:我住在杭州西湖边上,周末经常去断桥散步\n助手:西湖边真舒服' },
30 { key: 'home-new', scope: OWNER, time: at(40), content: '用户:我现在住在上海浦东了,上个月刚搬过来\n助手:新家怎么样?' },
31 { key: 'game', scope: GROUP, time: at(6), content: '用户:【新消息】\n小明:今晚LOL五排有人吗,我黄金二\n助手:我可以围观吗哈哈' },
32 { key: 'bili', scope: OWNER, time: at(7), content: '用户:我的B站账号叫咸鱼不想翻身,你可以去关注一下\n助手:已经记住你的B站名字了' },
33 { key: 'exam', scope: OWNER, time: at(8), content: '用户:下周三我要考驾照科目二,好紧张\n助手:倒车入库多练练,你可以的' },
34 { key: 'promise', scope: GROUP, time: at(9), content: '用户:【新消息】\n小红:你答应过要给我们唱一首晴天的\n助手:好好好,等我练好了就唱' },
35 { key: 'food', scope: OWNER, time: at(10), content: '用户:我不吃香菜,一点都不行,闻到就想吐\n助手:好的,以后推荐菜都避开香菜' },
36 { key: 'work-ask', scope: OWNER, time: at(11, 0), content: '用户:今天面试的结果出来了\n助手:怎么样怎么样?' },
37 { key: 'work-answer', scope: OWNER, time: at(11, 2), content: '用户:过了!下个月一号去字节报到,做前端\n助手:太棒了,恭喜!' },
38 { key: 'medicine', scope: OWNER, time: at(12), content: '用户:医生让我每天晚上九点吃一次降压药\n助手:我到点提醒你' },
39 { key: 'group-rule', scope: GROUP2, time: at(13), content: '用户:【新消息】\n群主:本群禁止发广告,发一次直接踢\n助手:收到,我会乖乖的' },
40 { key: 'secret', scope: OTHER, time: at(14), content: '用户:别告诉别人,我其实在准备辞职\n助手:放心,我不会说出去的' },
41 { key: 'plant', scope: OWNER, time: at(15), content: '用户:阳台那盆绿萝最近叶子发黄\n助手:可能是浇水太多了,等土干了再浇' },
42];
43
44export const queries = [
45 { kind: 'exact', scope: OWNER, q: '团子最近还好吗', expect: ['cat'], answer: '橘猫' },
46 { kind: 'exact', scope: OWNER, q: '还记得我生日是几号吗', expect: ['birthday'], answer: '3月15号' },
47 { kind: 'exact', scope: GROUP, q: '阿杰能吃花生吗', expect: ['allergy'], answer: '过敏' },
48 { kind: 'exact', scope: OWNER, q: '我B站叫什么来着', expect: ['bili'], answer: '咸鱼不想翻身' },
49 { kind: 'exact', scope: GROUP, q: '小明LOL什么段位', expect: ['game'], answer: '黄金二' },
50 { kind: 'exact', scope: OWNER, q: '降压药几点吃', expect: ['medicine'], answer: '九点' },
51 { kind: 'paraphrase', scope: OWNER, q: '我的猫叫啥名字', expect: ['cat'], answer: '团子' },
52 { kind: 'paraphrase', scope: OWNER, q: '科目二考得怎么样', expect: ['exam'], answer: '下周三' },
53 { kind: 'paraphrase', scope: GROUP, q: '你是不是欠我们一首歌', expect: ['promise'], answer: '晴天' },
54 { kind: 'paraphrase', scope: OWNER, q: '点外卖要注意我什么口味', expect: ['food'], answer: '香菜' },
55 { kind: 'paraphrase', scope: OWNER, q: '绿萝怎么救', expect: ['plant'], answer: '浇水' },
56 { kind: 'paraphrase', scope: GROUP, q: '群里能发广告吗', expect: ['group-rule'], answer: '踢' },
57 { kind: 'context', scope: OWNER, q: '周末去哪玩来着', expect: ['trip-ask', 'trip-decide'], answer: '八点' },
58 { kind: 'context', scope: OWNER, q: '面试结果怎么样了', expect: ['work-ask', 'work-answer'], answer: '字节' },
59 { kind: 'update', scope: OWNER, q: '你记得我住在哪吗', expect: ['home-new'], answer: '上海', older: 'home-old', olderAnswer: '杭州' },
60 { kind: 'noisy', scope: GROUP, q: '__BOILER__\n【新消息】\n小红:话说阿杰是不是有什么不能吃的\n__TAIL__', expect: ['allergy'], answer: '花生' },
61 { kind: 'noisy', scope: OWNER, q: '__BOILER__\n【新消息】\n主人:我的猫最近不爱吃饭\n__TAIL__', expect: ['cat'], answer: '团子' },
62 { kind: 'semantic', scope: OWNER, q: '宠物多大了', expect: ['cat'], answer: '三岁' },
63 { kind: 'semantic', scope: OWNER, q: '新工作在哪家公司', expect: ['work-answer'], answer: '字节' },
64];
65
66/** Long fixed instructions a bridge prepends to every prompt; the same each turn. */
67export const BOILER = [
68 '【当前角色】小鲸鱼(继续按角色卡说话)',
69 '【反AI味】你不是客服:不用有求必应,可以装傻、已读乱回、拒绝推荐;少用“一绝/真管用/保证/绝对”这类词。',
70 '【积极性】不要习惯性潜水:群里有你能接的话题就主动参与,偶尔插一句别人的话题也很正常;只有确实没话可说时才潜水。',
71 '【引用:只在必要时用】只有你这条消息指向的人或消息并非最新一条别人的消息时,才引用具体那条;其他情况不要引用。',
72 '【表情包】可以用表情包回复,但不要每句都发,一般三四句话里最多一个。吃饭睡觉上班这种日常话题随便聊。',
73].join('\n');
74export const TAIL = '请根据情况决定是否回复。如果不需要回应、想潜水/不接话,请只输出 [SILENT];否则正常回复。今天也要开开心心的哦。';
75
76const fillerLines = [
77 ['今天吃什么好呢', '要不点个外卖吧'], ['好困啊想睡觉', '那就早点睡'], ['上班好累', '摸摸头,辛苦了'],
78 ['哈哈哈哈笑死我了', '什么这么好笑'], ['周末有什么安排', '还没想好呢'], ['下雨了没带伞', '找个地方躲躲雨吧'],
79 ['刚吃完火锅好撑', '火锅永远的神'], ['你在干嘛呢', '在等你说话呀'], ['我最近在追一部剧', '什么剧呀好看吗'],
80 ['好想放假', '快了快了再坚持一下'], ['今天地铁好挤', '早高峰就是这样'], ['晚饭吃了面条', '面条也挺好的'],
81 ['我的猫粮快没了', '记得去买呀'], ['明天要早起', '那今晚别熬夜了'], ['这个月工资还没发', '再等等吧'],
82 ['周六好像要下雨', '那就在家休息'], ['我朋友今天生日', '记得说生日快乐'], ['医院排队排了好久', '辛苦啦'],
83 ['刚打完一把游戏', '赢了吗'], ['考试终于结束了', '恭喜解放'], ['在看B站视频', '看什么呢'],
84 ['想去海边看日落', '听起来好浪漫'], ['我住的地方好吵', '戴个耳塞试试'], ['晚上九点有个会', '会开完早点休息'],
85];
86/** Deterministic everyday chatter across all scopes, spread over the same weeks as the facts. */
87export function filler() {
88 const scopes = [OWNER, GROUP, GROUP2, OWNER, GROUP];
89 const out = [];
90 for (let i = 0; i < 240; i++) {
91 const [user, reply] = fillerLines[i % fillerLines.length];
92 const scope = scopes[i % scopes.length];
93 const speaker = scope.startsWith('group:') ? `【新消息】\n${['小明', '小红', '阿杰', '路人甲'][i % 4]}:` : '';
94 out.push({ key: 'filler-' + i, scope, time: at(1 + (i % 45), 30 + (i % 300)), content: `用户:${speaker}${user}\n助手:${reply}` });
95 }
96 return out;
97}