沉浸式角色聊天容易超时。这篇本地账本记录 start/end,超过软限制(默认 45 分钟)给出休息提示。不联网、不读取 App。
先看清成品
- start→end 写出分钟数;summary 汇总。
- 2 项 pytest 通过。
合规提醒:Character.AI 角色均为虚构内容,不代表真实人物,不能当作医疗/法律/金融等专业建议。平台有年龄限制与未成年人安全措施,请遵守官方规定。长时间聊天注意休息。官方 Help Center 表明目前没有面向开发者的公开 API;勿使用非官方逆向客户端(可能违反条款且不稳定)。本文不编造价格与会员权益。
运行说明:无 Character.AI API(官方亦无公开开发者 API)。三篇均为本地助手:角色定义校验、会话时长账本、导出/提示词虚构免责扫描。未登录 character.ai。2026-10-23(Asia/Shanghai)pytest 通过。
做完之后你会得到什么?
| 成果 | 说明 |
|---|---|
session_timer.py |
时长账本 |
test_106b.py |
测试 |
需要准备哪些环境?
| 项目 | 说明 |
|---|---|
| Python | 3.10+ |
关键事实
| 事实 | 来源 |
|---|---|
| 产品建议注意休息与数字健康 | 常见安全提示 |
| 本工具与 Character.AI 服务器无关 | 重要 |
| 软限制可按个人调整 --limit-min | 实践 |
文件结构
106-character-ai-B-src/
├── session_timer.py
└── test_106b.py
第 1 步:计时脚本
#!/usr/bin/env python3
"""Local wellbeing timer for long Character.AI chat sessions (offline; no API)."""
from __future__ import annotations
import argparse
import json
from datetime import datetime, timezone
from pathlib import Path
DEFAULT_LIMIT = 45
def load(path: Path) -> dict:
if path.exists():
return json.loads(path.read_text(encoding="utf-8"))
return {"sessions": []}
def start(ledger: dict, label: str) -> dict:
now = datetime.now(timezone.utc).isoformat()
ledger.setdefault("sessions", []).append({"label": label, "started": now, "ended": None})
return {"action": "start", "label": label, "started": now}
def end(ledger: dict, limit_min: int) -> dict:
open_s = [s for s in ledger.get("sessions") or [] if not s.get("ended")]
if not open_s:
return {"action": "end", "error": "无进行中的会话"}
s = open_s[-1]
now = datetime.now(timezone.utc)
s["ended"] = now.isoformat()
started = datetime.fromisoformat(s["started"])
minutes = (now - started).total_seconds() / 60.0
warn = minutes >= limit_min
return {
"action": "end",
"label": s.get("label"),
"minutes": round(minutes, 1),
"over_soft_limit": warn,
"message": "建议休息一下眼睛与手腕" if warn else "时长在建议范围内",
}
def summary(ledger: dict) -> dict:
total = 0.0
n = 0
for s in ledger.get("sessions") or []:
if s.get("started") and s.get("ended"):
total += (
datetime.fromisoformat(s["ended"]) - datetime.fromisoformat(s["started"])
).total_seconds() / 60.0
n += 1
return {"closed_sessions": n, "total_minutes": round(total, 1)}
def main(argv=None) -> int:
ap = argparse.ArgumentParser(description=__doc__)
ap.add_argument("--ledger", default="cai_sessions.json")
sub = ap.add_subparsers(dest="cmd", required=True)
s = sub.add_parser("start")
s.add_argument("label", nargs="?", default="chat")
e = sub.add_parser("end")
e.add_argument("--limit-min", type=int, default=DEFAULT_LIMIT)
sub.add_parser("summary")
args = ap.parse_args(argv)
path = Path(args.ledger)
ledger = load(path)
if args.cmd == "start":
out = start(ledger, args.label)
elif args.cmd == "end":
out = end(ledger, args.limit_min)
else:
out = summary(ledger)
path.write_text(json.dumps(ledger, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
print(json.dumps(out, ensure_ascii=False, indent=2))
return 0
if __name__ == "__main__":
raise SystemExit(main())
第 2 步:测试
from pathlib import Path
import json, subprocess, sys, time
from session_timer import start, end, load
HERE = Path(__file__).resolve().parent
def test_flow(tmp_path):
led = {"sessions": []}
start(led, "demo")
time.sleep(0.05)
info = end(led, limit_min=999)
assert info["minutes"] >= 0 and info["over_soft_limit"] is False
def test_cli(tmp_path):
led = tmp_path / "l.json"
subprocess.run([sys.executable, str(HERE/"session_timer.py"), "--ledger", str(led), "start", "x"], check=True)
r = subprocess.run([sys.executable, str(HERE/"session_timer.py"), "--ledger", str(led), "end", "--limit-min", "999"], capture_output=True, text=True, check=True)
assert "minutes" in r.stdout
第 3 步:运行
$ session_timer
{
"action": "start",
"label": "demo",
"started": "2026-10-23T04:57:54.430674+00:00"
}
{
"action": "end",
"label": "demo",
"minutes": 0.0,
"over_soft_limit": false,
"message": "时长在建议范围内"
}
{
"closed_sessions": 1,
"total_minutes": 0.0
}
$ pytest -q
.. [100%]
2 passed in 0.11s
怎么检查结果?
- end 含 minutes 与 message。
- summary 统计 closed_sessions。
调试示例
忘了 end。 summary 不会计入未关闭会话。养成结束聊天就跑 end 的习惯,或用系统番茄钟双保险。
常见问题
| 现象 | 原因 | 处理 |
|---|---|---|
| 多角色并行 | 多次 start | 分别 end |
| 改 45 分钟 | end --limit-min | 按需 |
| 要云同步 | 自行备份 ledger | 勿含隐私对话全文 |
模板规则
- 超时就休息。
- 账本勿提交含敏感对话的仓库。
- 遵守平台安全提示。
项目资源
| 用途 | 地址 |
|---|---|
| About(官方) | https://character.ai/about |
| Blog(官方) | https://blog.character.ai/ |
角色可以陪聊,身体不能透支。