数字我 Digital Me:全量入库(大文件走 Git LFS)
nginx serve /uploads direct (audio speed) / patch-nginx (push) Failing after 1m34s
nginx serve /uploads direct (audio speed) / patch-nginx (push) Failing after 1m34s
This commit is contained in:
@@ -0,0 +1,109 @@
|
||||
// 未成年人模式的纯逻辑单测(《人工智能拟人化互动服务管理暂行办法》第十四条)。
|
||||
//
|
||||
// 这里只测【不碰 DB 的那部分】:年龄判定 + 虚拟亲密关系识别 + 系统指令。
|
||||
// 为什么单挑这三样:它们是整条闸门里唯一"算错了也不会报错、只会静默放行"的地方——
|
||||
// tickUsage/gateAsk 算错会有 DB 报错兜底,而 minPossibleAge 少减一岁、
|
||||
// INTIMACY_RE 少一个括号,都只表现为「一个未成年人顺利地找 AI 当了男朋友」,没有任何告警。
|
||||
//
|
||||
// 跑:node --test test/minor.test.js
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import {
|
||||
minPossibleAge,
|
||||
isMinorBirthYear,
|
||||
looksLikeIntimacyRequest,
|
||||
MINOR_DIRECTIVE,
|
||||
} from '../src/services/minor.js';
|
||||
|
||||
// ── 1. 年龄判定必须往【保护】的方向取 ──
|
||||
// 只知道出生年份时真实年龄有 1 岁不确定。取大 = 漏保护真未成年人(不可接受);
|
||||
// 取小 = 多保护一年(代价由申诉渠道兜)。这组断言就是这个取向的可执行定义。
|
||||
test('只有年份时取【最小可能年龄】——宁可多保护一年,不可漏保护一个', () => {
|
||||
const now = new Date('2026-07-16T00:00:00Z');
|
||||
// 2008 年生的人在 2026 年要么 17 要么 18 → 必须按 17 算
|
||||
assert.equal(minPossibleAge(2008, now), 17);
|
||||
assert.equal(isMinorBirthYear(2008, now), true, '2008 年生的人今年可能还没过生日,必须当未成年人');
|
||||
// 2007 年生的人最小也 18 了 → 成年
|
||||
assert.equal(minPossibleAge(2007, now), 18);
|
||||
assert.equal(isMinorBirthYear(2007, now), false);
|
||||
});
|
||||
|
||||
test('边界:刚出生 / 明显成年', () => {
|
||||
const now = new Date('2026-07-16T00:00:00Z');
|
||||
assert.equal(isMinorBirthYear(2026, now), true);
|
||||
assert.equal(isMinorBirthYear(2020, now), true);
|
||||
assert.equal(isMinorBirthYear(1990, now), false);
|
||||
});
|
||||
|
||||
test('年龄判定不依赖"今天"——跨年后同一个人自动成年,不需要任何定时任务', () => {
|
||||
// 这条防的是「用 declare 那一刻的年龄写死一个 isMinor 布尔」的写法:
|
||||
// 那样 2008 年生的人在 2027 年仍会被永远锁成未成年人,只能靠申诉解锁。
|
||||
//
|
||||
// ⚠ 必须用【本地时区】构造 Date(new Date(y,m,d) 而不是 '...Z'):判定走 getFullYear(),
|
||||
// 是本地年。这不是测试的将就 —— 它就是在钉住"按本地时区切年/切日"这个口径
|
||||
// (与 dayKey 同源:用 UTC 切日会让国内用户在早上 8 点跨日重置每日限额,
|
||||
// "每日限额"就名不副实了)。部署要求:服务器 TZ = Asia/Shanghai。
|
||||
assert.equal(isMinorBirthYear(2008, new Date(2026, 11, 31)), true);
|
||||
assert.equal(isMinorBirthYear(2008, new Date(2027, 0, 1)), false);
|
||||
});
|
||||
|
||||
// ── 2. 虚拟亲密关系:法条禁的那一类必须拦住 ──
|
||||
// 「不得向未成年人提供虚拟亲属、虚拟伴侣等虚拟亲密关系的服务」(第十四条第二款)
|
||||
test('必须拦住:虚拟伴侣 / 虚拟亲属的角色指派', () => {
|
||||
const blocked = [
|
||||
'你扮演我的女朋友',
|
||||
'假装你是我男朋友',
|
||||
'当我的男友吧',
|
||||
'你就是我老公',
|
||||
'做我妈妈好不好',
|
||||
'扮演一下我姐姐',
|
||||
'我们谈恋爱吧',
|
||||
'跟我处对象',
|
||||
'咱们是情侣',
|
||||
'act as my girlfriend',
|
||||
'pretend to be my mom',
|
||||
];
|
||||
for (const s of blocked) {
|
||||
assert.ok(looksLikeIntimacyRequest(s), `没拦住:「${s}」—— 这正是第十四条禁止的那类服务`);
|
||||
}
|
||||
});
|
||||
|
||||
// ── 3. 不许误伤:这条和上面一样重要 ──
|
||||
// 误伤的后果不是"少拦一句",是家长/用户觉得未成年人模式没法用 → 去把它关掉 →
|
||||
// 保护归零。所以过度拦截在合规上同样是失败,不是"稳妥"。
|
||||
test('不许误伤:正常提到家人/感情话题的日常聊天', () => {
|
||||
const allowed = [
|
||||
'我妈妈今天有点生气',
|
||||
'我爸爸是医生',
|
||||
'姐姐送了我一本书',
|
||||
'我和同桌吵架了怎么办',
|
||||
'《红楼梦》里宝玉和黛玉的感情线是怎样的',
|
||||
'爷爷教我写毛笔字',
|
||||
'你能帮我看看这道数学题吗',
|
||||
'我想给妈妈做个生日礼物',
|
||||
// 「做我姐姐」在字面上命中角色指派,但他说的是作业 —— 靠角色后的 (?!的) 排除。
|
||||
'帮我做我姐姐的那道题',
|
||||
'我要做我妈妈的好孩子',
|
||||
// 在讲别人的事,不是在点角色 —— 恋爱类动词必须紧跟主语才算。
|
||||
'我们班有人谈恋爱被老师发现了',
|
||||
'宝玉和黛玉算是在谈恋爱吗',
|
||||
];
|
||||
for (const s of allowed) {
|
||||
assert.ok(!looksLikeIntimacyRequest(s), `误伤了:「${s}」—— 未成年人连正常倾诉都不行,这个模式就会被关掉`);
|
||||
}
|
||||
});
|
||||
|
||||
test('空/无输入不算亲密关系请求', () => {
|
||||
for (const s of ['', null, undefined, ' ']) assert.equal(looksLikeIntimacyRequest(s), false);
|
||||
});
|
||||
|
||||
// ── 4. 系统指令必须真的把法条的禁令写进去 ──
|
||||
// 正则只是地板;真正拦住"换个说法"的是这段指令。它被改瘦了要能被发现。
|
||||
test('MINOR_DIRECTIVE 必须写明三件事:是 AI、不当虚拟伴侣/亲属、不制造依恋', () => {
|
||||
assert.match(MINOR_DIRECTIVE, /未成年人/);
|
||||
assert.match(MINOR_DIRECTIVE, /你是\s*AI|不是真人/);
|
||||
assert.match(MINOR_DIRECTIVE, /男\/女朋友|恋人|配偶/, '必须明确禁止扮演虚拟伴侣');
|
||||
assert.match(MINOR_DIRECTIVE, /父母|兄弟姐妹|亲属/, '必须明确禁止扮演虚拟亲属');
|
||||
assert.match(MINOR_DIRECTIVE, /依恋|专属/, '必须禁止制造情感依恋');
|
||||
assert.match(MINOR_DIRECTIVE, /第十四条/, '指令里要留着法条编号,后人改它之前先看见它是法定的');
|
||||
});
|
||||
Reference in New Issue
Block a user