diff --git a/.gitignore b/.gitignore index 01dab49..2f6371a 100644 --- a/.gitignore +++ b/.gitignore @@ -137,3 +137,5 @@ __pycache__/ *.py[cod] /nul /.playwright-cli/ +/docs/ +/scripts/ diff --git a/FavorabilitySystem.md b/FavorabilitySystem.md deleted file mode 100644 index 63456c1..0000000 --- a/FavorabilitySystem.md +++ /dev/null @@ -1,65 +0,0 @@ -# 好感度与主观印象系统 - -## 功能定位 - -好感度系统保存 Bot 对群友的主观认识,与证据驱动的长期用户画像互补: - -- `FavorabilityInfo` 保存好感度、代号、标签、印象和最近调整原因; -- `user-profile.sqlite` 保存从历史聊天归纳出的长期画像; -- 两套数据独立持久化,在普通对话上下文中按用户合并渲染; -- 自动历史画像不会修改主观好感度。 - -## 数据结构 - -```kotlin -@Serializable -data class FavorabilityInfo( - val userId: Long, - val value: Int = 0, - val reasons: List = emptyList(), - val impression: String = "", - val name: String = "", - val tags: List = emptyList(), -) -``` - -字段约束: - -- `value`:限制在 -100 到 100; -- `reasons`:只在好感度发生变化且提供原因时追加,保留最近 10 条; -- `impression`:最多 200 字符; -- `name`:最多 20 字符; -- `tags`:最多 5 项,每项最多 20 字符。 - -数据继续保存在 Mirai `data.yml` 中,已有字段保持兼容。 - -## AI 更新工具 - -`adjustUserFavorability` 是唯一的好感度和主观印象维护入口。模型可以在一次调用中: - -- 通过 `change` 增减好感度; -- 覆盖 `impression` 或 `name`; -- 通过 `tags_add`、`tags_remove` 调整标签。 - -只更新印象或标签时,`change` 默认为 0。系统不会因为好感度为 0 而删除用户记录。 - -## 回复门控 - -启用 `enableFavorabilitySystem` 后,负好感度会降低对应用户触发 Bot 回复的概率: - -```text -忽略概率 = abs(value) / 100 -``` - -好感度不再随时间自动向 0 偏移,也没有管理员手动修改或清空好感度的命令。它只会在模型明确调用工具时变化。 - -## 上下文注入 - -普通聊天会把当前相关用户的主观认识和长期画像合并为紧凑文本: - -- 群聊优先选择触发者和最近发言者; -- 私聊只注入当前联系人; -- 仅有数值、没有代号/标签/印象的空记录不会制造提示词噪声; -- 好感度为 0 但仍有代号、标签或印象的记录继续正常注入。 - -长期画像的生成、证据校验和自动维护流程参见 `ProfileSystemDesign-v3.md`。 diff --git a/scripts/recover_data_yml.py b/scripts/recover_data_yml.py deleted file mode 100644 index e995b02..0000000 --- a/scripts/recover_data_yml.py +++ /dev/null @@ -1,72 +0,0 @@ -#!/usr/bin/env python3 -""" -恢复 data.yml:把 tokenUsageDailyRecords 抽出成 token_usage.json, -顺手清理 tokenUsageRecords,把 data.yml 重写成合法的、yamlkt 能读回的 JSON。 - -用法(在 data.yml 所在目录运行): - python3 recover_data_yml.py /path/to/top.jie65535.mirai.JChatGPT/ - -会做: - 1. 备份原 data.yml -> data.yml.bak- - 2. 读 data.yml(按 JSON 解析,目前文件就是 JSON-flow YAML) - 3. 把 tokenUsageDailyRecords 写到 token_usage.json - 4. 删除 tokenUsageRecords 和 tokenUsageDailyRecords 字段 - 5. 重写 data.yml(保留 contactMemory / userFavorability 等) -""" -import json -import os -import sys -import time - -def main(target_dir: str) -> int: - data_path = os.path.join(target_dir, "data.yml") - if not os.path.exists(data_path): - print(f"NOT FOUND: {data_path}", file=sys.stderr) - return 1 - - with open(data_path, "r", encoding="utf-8") as f: - text = f.read() - - try: - data = json.loads(text) - except json.JSONDecodeError as e: - print(f"data.yml 不是合法 JSON:{e}", file=sys.stderr) - print("如果文件其实是 block-style YAML,请先用 yq/python yaml 转换", file=sys.stderr) - return 2 - - if not isinstance(data, dict): - print(f"顶层不是 map,是 {type(data).__name__}", file=sys.stderr) - return 3 - - ts = int(time.time()) - backup_path = os.path.join(target_dir, f"data.yml.bak-{ts}") - with open(backup_path, "w", encoding="utf-8") as f: - f.write(text) - print(f"已备份 -> {backup_path}") - - daily_records = data.pop("tokenUsageDailyRecords", []) - raw_records = data.pop("tokenUsageRecords", []) - print(f"提取 tokenUsageDailyRecords: {len(daily_records)} 条") - print(f"丢弃 tokenUsageRecords (legacy): {len(raw_records)} 条") - - token_path = os.path.join(target_dir, "token_usage.json") - if os.path.exists(token_path): - token_backup = os.path.join(target_dir, f"token_usage.json.bak-{ts}") - os.rename(token_path, token_backup) - print(f"已备份现有 token_usage.json -> {token_backup}") - - with open(token_path, "w", encoding="utf-8") as f: - json.dump(daily_records, f, ensure_ascii=False, indent=2) - print(f"写入 -> {token_path} ({len(daily_records)} 条)") - - with open(data_path, "w", encoding="utf-8") as f: - json.dump(data, f, ensure_ascii=False, indent=4) - print(f"重写 -> {data_path}(剩余字段: {list(data.keys())})") - return 0 - - -if __name__ == "__main__": - if len(sys.argv) != 2: - print(__doc__, file=sys.stderr) - sys.exit(1) - sys.exit(main(sys.argv[1]))