内容
# 🧠 TermLoop
> 术语不断重复,直到你记住它们。
一个最小化的 Telegram 机器人,用于专业术语的间隔重复 -
此外,还包括一个 MCP 服务器,这样你可以直接在与 LLM(Claude Code、Claude Desktop 或其他支持 [MCP](https://modelcontextprotocol.io) 的模型)的对话中,将术语保存到你的词典中。
**文档:** [gyxer513.github.io/termloop](https://gyxer513.github.io/termloop/) · [俄语文档](docs/README.ru.md)
## 为什么
问题往往不是缺乏工程理解,而是专业词汇滞后于你已经在实践中使用的概念。
你知道“重新处理不能改变结果”;TermLoop 会确保“幂等性”这个词不断重复,直到你记住它。
无需 AI 评分,无需花费代币:你通过两个按钮自我评估,
优先级数学是确定性的。
## 功能
- 个人卡片词典(术语 - 定义,可选主题)
- 优先级轮换:新卡片和遗忘的卡片更频繁地重复
(100 → 减少 1/10/25/50 每次回忆;“遗忘”重置为 100)
- 自我评估流程:显示术语 → 回忆 → 显示定义 → 记住 /
遗忘
- 手动复习(`/go [主题]`)和共享通知计划
- MCP 服务器(可流式 HTTP)带有 `add_term` / `list_terms` /
`list_terms_topics` — 从任何 LLM 聊天中写入术语
- 稳健设计:状态机,单次使用审查令牌和 TTL,
幂等回调,每次查询的行级所有权,速率限制,
可选的 Telegram ID 允许列表
- 无聊运维:单进程,长轮询(无公共端点),SQLite WAL,
Alembic 迁移,Docker Compose
## 机器人命令
| 命令 | 操作 |
|---|---|
| `/start` | 创建账户,显示帮助 |
| `/add 主题 \| 术语 \| 定义` | 添加卡片(主题可选) |
| `/edit 17 \| 主题 \| 术语 \| 定义` | 编辑卡片 #17 |
| `/delete 17` | 删除卡片 #17 |
| `/list [主题]` | 列出你的卡片 |
| `/topics` | 列出主题 |
| `/go [主题]` | 开始或恢复复习 |
| `/notify on\|off` | 切换提醒 |
| `/cancel` | 重置活动尝试 |
## 快速开始
要求:Docker + Compose,从
[@BotFather](https://t.me/BotFather) 获取的机器人令牌。
```bash
git clone https://github.com/Gyxer513/termloop.git
cd termloop
mkdir -p data && sudo chown 1000:1000 data # 容器以 uid 1000 运行
cp .env.example .env
# 在 .env 中设置 BOT_TOKEN、MCP_TELEGRAM_USER_ID、MCP_AUTH_TOKEN
docker compose up -d --build
```
配置(`.env`):
| 变量 | 默认值 | 目的 |
|---|---|---|
| `BOT_TOKEN` | — | Telegram 机器人令牌(必填) |
| `REVIEW_TIMES` | `10:00,19:00` | 共享提醒计划 |
| `TIMEZONE` | `Europe/Moscow` | 计划时区 |
| `PENDING_TTL_MINUTES` | `30` | 活动尝试过期 |
| `ALLOWED_TELEGRAM_IDS` | 空 | 可选允许列表(空 = 所有人) |
| `MCP_TELEGRAM_USER_ID` | — | MCP 工具写入的词典用户 ID(MCP 必填) |
| `MCP_AUTH_TOKEN` | 空 | MCP 端点的静态 bearer 令牌 |
| `MCP_PORT` | `8210` | MCP 服务器端口 |
## MCP:从 LLM 对话中保存术语
`termloop-mcp` 服务在 `http://<host>:8210/mcp`
(可流式 HTTP,bearer 认证)上公开词典。卡片进入机器人使用的相同轮换中。
Claude Code:
```bash
claude mcp add --transport http termloop http://<host>:8210/mcp \
--header "Authorization: Bearer <MCP_AUTH_TOKEN>"
```
Claude Desktop(`claude_desktop_config.json`,通过本地 `mcp-remote`
桥接器 - claude.ai 本身的连接器需要公共 HTTPS URL):
```json
"termloop": {
"command": "npx",
"args": ["-y", "mcp-remote", "http://<host>:8210/mcp",
"--allow-http", "--header", "Authorization:${AUTH_HEADER}"],
"env": { "AUTH_HEADER": "Bearer <MCP_AUTH_TOKEN>" }
}
```
然后直接说: *"保存到 termloop:bulkhead — 资源隔离,以便一个失败的组件不会影响其他组件,主题为 Architecture。"*
## 开发
Python 3.12+,无需外部服务:
```bash
python -m venv .venv && . .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -r requirements-dev.txt
pytest # 34 个测试:优先级策略、状态机、所有权、调度器、迁移
ruff check .
```
技术栈: [aiogram 3](https://github.com/aiogram/aiogram) ·
SQLAlchemy 2(异步)· Alembic · APScheduler ·
[MCP Python SDK](https://github.com/modelcontextprotocol/python-sdk) ·
SQLite(WAL)。
布局:领域逻辑位于 `app/services/`(~280 行,完全覆盖测试),
Telegram 连接在 `app/bot/`,MCP 服务器在 `app/mcp_server.py`。
## 设计笔记
- 两个触发器 — `/go` 和调度器 — 调用相同的
`start_review(user, topic, source)` 应用服务。
- 卡片选择:前 10 个按优先级排序,带有确定性 tie-breaks,应用内随机选择。
无 `ORDER BY RANDOM()`。
- 复习状态机:`IDLE → QUESTION_SHOWN → ANSWER_SHOWN → IDLE`,
由每个尝试的随机令牌保护;过时或重放的回调是安全的无操作。
- 授权是基于行的,存在于查询本身中:外部卡片与缺失的卡片无法区分。
- 停止规则是规范的一部分:无 AI 评分,无 Web UI,无标签系统,
无分析,直到实际使用需求出现。
## 问题和反馈
在 [讨论(Q&A)](https://github.com/Gyxer513/termloop/discussions) 中提问 —
错误报告请前往 [问题](https://github.com/Gyxer513/termloop/issues)。
## 许可证
[MIT](LICENSE)