Lee's Blog
  • 首页
  • 博客
  • 作品
  • 成长
  • 学习
  • 打榜
  • 练习
  • 关于
登录 / 注册
✦

保持联系

关注我的最新动态

GitHubbilibili

© 2026 Lee's Blog

首页/博客/文档/重构文档
重构文档
文档2026年5月22日17 分钟阅读

重构文档

架构
X微博

AI Diary 重构计划

最后更新:2026-05-22 | 版本 0.1.1-dev


目录

  1. 项目健康评估
  2. 架构诊断
  3. Bug 清单
  4. 优化清单
  5. 重构路线图
  6. 架构目标

1. 项目健康评估

概览

维度 状态 说明
P0 阻塞级 Bug ✅ 全部清除 输入框卡顿、429 误禁、重复归档、历史不可见
P1 高优 Bug/优化 ⚠️ 过半完成 会话恢复、Provider 重启用等 3 完成 + 4 部分 + 4 未/新增
P2 体验改善 ⚠️ 少量完成 侧边栏信息密度已优化;搜索、日志、连接复用待定
P3 代码整洁 ⚠️ 少数顺手修了 dead code 等基本未动
测试覆盖 ❌ 0% 无任何测试
日志系统 ❌ 无 异常被静默吞噬,排查困难
架构解耦 ❌ 差 837 行 God Object,UI/业务/命令混在一起

功能缺失清单

以下核心功能尚未实现:

  • 文本编辑:历史消息不可编辑、无 /edit 命令
  • 自动归档:日期间隔不会自动归档,换天手动 Ctrl+G
  • 上下文注入:仅注入日期和标题列表,策略粗糙,AI 不知道如何有效利用

进度统计

P0 ■■■■■ 5/5 清除
P1 ■■■■□ 7/11(3 完成 + 4 部分 + 3 未开始 + 1 新增)
P2 ■□□□□ 1/8
P3 ■■□□□ 3/8
─────────────────
总计 15/32 完成或部分完成

2. 架构诊断

2.1 当前架构

app.py (837 行 God Object)
├── UI 渲染 ── Textual widgets, CSS 内联字符串 → 已抽至 diary.tcss
├── 命令解析 ── 字符串 split + 巨型 if/elif 链 (180+ 行)
├── 命令自动执行 ── 正则从 AI 回复中挖 /command (70 行)
├── 业务逻辑 ── 归档、Provider、目标、倒数日分散各处
├── 错误处理 ── 混在 UI 层,大量 except:pass
└── 状态管理 ── self 属性散落(_awaiting_provider, _archiving...)

2.2 核心矛盾

每增加一个 /命令,需要修改 3 个地方:

位置 改什么
_handle_command 加 elif cmd == "/xxx" 分支
_handle_command 上半部分 改 /help 硬编码字符串列表
_auto_execute_commands 同步一份解析逻辑

三处格式对不齐就出 Bug,维护成本随命令数量线性增长。

2.3 关键缺失流程

文本编辑

当前消息一旦发送不可更改。用户打错字、想删除某句话都无法操作。这是日记工具的基本能力——写作必然伴随修改。

CLI Creator 类比:相当于只有 create 没有 update/delete。

自动归档

用户昨天写的日记,今天打开 App 不会自动归档。需手动 Ctrl+G。隔三天打开,三天前的日记还挂着 active。

CLI Creator 类比:缺少 doctor 风格的启动检查——"startup 时扫描状态,把该收尾的收尾"。

上下文注入

当前 build_messages 把历史消息全量灌入(无截断风险),注入的上下文(目标、倒数日、摘要日期)只有标题列表,LLM 不知道如何有效利用这些信息来提问。

CLI Creator 类比:相当于 --help 只列出命令名,没有用法示例和最佳实践提示。

2.4 架构目标

┌─ TUI 层 ────────── 薄层,只有渲染和事件绑定
├─ 命令注册表 ────── 一处定义,自动生成 help + 校验 + 分发
└─ 操作层 ────────── 纯函数/类,独立可测,不依赖 Textual
    ├── SessionService    ├── ArchiveService
    ├── GoalService       ├── CountdownService
    ├── ProviderService   ├── SearchService
    ├── DoctorService     ├── EditService
    ├── MemoryService     (Mem0 记忆层)
    └── ContextService    (整合记忆 + 目标的上下文引擎)

3. Bug 清单

3.1 已修复

# Bug 文件 修复内容
1 mkdir 缺 parents=True storage/txt.py:13 改为 mkdir(parents=True, exist_ok=True)
2 归档总标记 summary_edited=True archive/manager.py:22 直接设 summary_edited=False
4 429 永久禁用 Provider error_handler.py + sqlite.py 拆分 402=quota, 429=rate_limit
5 Ctrl+S 重复归档 ui/app.py:854 _archiving 锁
6 not_found 关键词过宽 error_handler.py:24 从列表中移除
7 Provider 无法重新启用 sqlite.py + app.py /provider enable <name>
8 输入框提交后卡住 ui/app.py:234 event.input.value = "" 移至 await 前
9 空会话堆积 sqlite.py + app.py abandon_empty_sessions()
10 幽灵测试数据 DB 清理 删除残留会话 + txt

3.2 待修复

# Bug 严重度 文件 根因
3 _with_retry else 分支不可达 低 llm/pool.py:47 last_error 在所有出循环路径上已赋值

4. 优化清单

4.1 已完成

# 优化 类型
4 会话恢复 功能
7 base_url 重复 strip 整洁
9 .gitignore 工程
12 /history 历史浏览 功能
14 侧边栏信息密度 功能
15 归档后翻阅 功能

4.2 部分完成

# 优化 已完成 缺失
11 首次使用体验 无 Provider 时显示引导 非向导式,仍填键值对
13 AI 记忆 注入日记日期列表 + 目标 + 倒数日 不注入摘要内容,记忆不深
19 提示词设计 System prompt 重写、Archive prompt 重构 上下文注入策略粗糙(详见 §5.4)
20 核心原则 去拟人化、三层追问框架 工具调用用行内命令代替 API
21 工具调用架构 _auto_execute_commands 行内解析 无 tool calling API、无确认机制

4.3 未开始

# 优化 影响 依赖
1 Token 截断 高 简单轮数截断即可
2 DB 连接复用 低 SQLite 单用户场景开销可忽略
3 LlmProvider dead code 低 —
5 /goal add 支持类型 中 需改命令格式
6 流式重试 中 _with_retry 改造
8 日志系统 高 需全项目引入 logging
10 侧边栏全表查询 低 —
16 搜索过滤 低 单人本地日记写作,全文搜索非刚需
17 统计洞察 中 需数据聚合
18 Provider 配置向导 中 需交互式输入流程

4.4 新增优化项

# 优化 影响 说明
22 文本编辑 高 消息历史可编辑、删除、重新生成 AI 回复
23 自动归档 高 启动时检测日期间隔,自动归档昨日/多日前活跃会话
24 上下文注入重构 高 从标题列表注入升级为结构化注入,给 LLM 明确的使用指引
25 流式对话接入 UI 中 LlmPool.stream_chat() 已实现,app.py 未使用,仍为"等待全文"体验
26 记忆系统 (Mem0) 高 基于 Mem0 的持久记忆层,语义检索、跨会话回忆

5. 重构路线图

Phase 1: 止血 —— 架构解耦基础

目标:消灭 God Object 的前 50% 毒性

1.1 命令注册表

步骤 产出 收益
新建 src/commands/registry.py 命令注册、解析、help 自动生成 消除 _handle_command 180 行 if/elif
迁移现有命令 每个命令独立模块 _auto_execute_commands 复用同一注册表
输入校验层 参数 schema、类型自动校验 /goal add 不再需硬编码类型
目标结构:
src/commands/
├── registry.py       # CommandRegistry: 注册、解析、help
├── base.py           # Command 基类: name, help, schema, execute()
├── goals.py          # GoalCommands
├── providers.py      # ProviderCommands
├── countdowns.py     # CountdownCommands
├── history.py        # HistoryCommands
├── sessions.py       # SessionCommands
├── memory.py         # MemoryCommands (新增): /memory search
├── edit.py           # EditCommands (新增): /edit delete, /edit retry
├── doctor.py         # DoctorCommand (新增)

注意:Phase 2 的 /edit 命令应基于此注册表实现,不继续扩展 app.py 的 if/elif 链。

1.2 操作层剥离

将 app.py 中散落的业务逻辑抽成 service:

src/services/
├── session.py     # 从 app.py _init_session, _create_new_session 抽
├── archive.py     # 从 app.py _do_archive, archive/manager.py 整合
├── provider.py    # 从 app.py _parse_provider_input 抽
├── context.py     # 新增: 上下文构建引擎 (从 prompt/engine.py 升级)
├── doctor.py      # 新增: DB、config、provider 全量健康检查
└── __init__.py

1.3 Doctor 命令

/doctor
  ├── Database    ✓ connected (diary.db, 10 sessions)
  ├── Config      ✓ ~/.ai-diary/config.json
  ├── Diary Dir   ✓ /Users/xxx/.ai-diary/diary/
  ├── Providers   ✗ DeepSeek (429 rate limited)
  │               ✓ OpenAI (active, quota: 850)
  └── Prompts     ✓ ~/.ai-diary/prompts/prompts.json

Phase 2: 回血 —— 核心体验补齐

2.1 文本编辑 (Opt 22)

提供类似聊天应用的消息编辑能力,使用 CLI Creator 的"发现→选中→操作"模式:

/edit 3                    # 编辑第 3 条消息(弹出编辑区)
/edit 4 "修正后的内容"      # 直接替换第 4 条消息
/edit delete 5             # 删除第 5 条消息及之后的所有回复
/edit retry 6              # 从第 6 条消息位置重新生成 AI 回复

设计要点:

  • 消息索引从 1 开始(用户可见顺序)
  • 删除消息后,之后的 AI/用户消息级联删除(因为上下文断裂)
  • /edit retry N 保留前 N-1 条,从第 N 条位置重新调用 LLM
  • 编辑后自动更新会话展示,同时触发侧边栏刷新

CLI Creator 视角:编辑是一个"写操作",应该接受最小资源 ID(消息索引)和操作类型,返回结果给调用方展示。UI 只负责渲染编辑前后的状态。

2.2 自动归档 (Opt 23)

启动时智能检测,自动处理日期跨越:

_startup_check():
    active = get_active_session()
    if not active or not active.messages:
        return  # 空会话,不处理

    session_date = parse_date(active.created_at)
    today = date.today()

    if session_date != today:
        # 日期变了,自动归档旧会话
        archive_session(active.id)
        create_new_session()
        show_notification(f"已自动归档 {session_date} 的日记")

设计要点:

  • 仅在 App 启动时执行一次(on_mount 末尾)
  • 只归档有消息的活跃会话,空会话已在 Bug 9 中清理
  • 归档后创建新会话,用户看到的是空白对话 + 归档通知
  • 不自动归档当日会话(用户可能还在写)
  • 多天未打开(例如 3 天):只归档最早的会话,其他活跃会话状态保持不变(用户可通过 /history 手动查阅后决定)

CLI Creator 视角:相当于 doctor 的健康检查扩展——startup 时扫描状态,对不健康的条目执行自动修复并报告。

2.3 记忆系统 (Opt 26) ⬅️ 新增

当前 AI 的"记忆"仅靠上下文注入日期列表(prompt/engine.py:42 "仅知日期,不知内容"),LLM 不知道用户昨天具体写了什么,也无法跨会话回忆过往事件。

方案:基于 Mem0(56.4k stars, Apache 2.0)构建持久记忆层。

为什么选 Mem0
约束 Mem0 其他
Python 库模式(无 server) ✅ 仅 cognee 部分支持
SQLite 存储 ✅ mem0_history.db Letta/Zep 需 Postgres/Neo4j
OpenAI 兼容 API ✅ 均支持
DeepSeek(中文 LLM) ✅ 需自行配置
本地无 Docker ✅ Letta/Zep 需要

唯一短板:默认英文 embedding 模型,需手动配置 BAAI/bge-large-zh-v1.5 来保证中文语义搜索质量。

配置
from mem0 import Memory

config = {
    "vector_store": {
        "provider": "chroma",          # SQLite 后端,零额外服务
        "config": {"path": "~/.ai-diary/chroma"}
    },
    "llm": {
        "provider": "openai",          # 复用现有 DeepSeek
    },
    "embedder": {
        "provider": "huggingface",
        "config": {"model": "BAAI/bge-large-zh-v1.5"}
    },
    "history_db_path": "~/.ai-diary/mem0_history.db"
}
新增依赖
mem0ai >= 0.1.0
chromadb >= 0.5.0
sentence-transformers >= 3.0.0
调用时机
每轮对话后:
  user_input → LLM 回复 → memory.add(本轮关键事实)

每次归档时:
  Ctrl+G → 生成摘要 → memory.add(摘要全文)

对话开始时:
  build_context → memory.search(当前话题) → 注入 context buffer

用户主动查询:
  /memory search "上次提到 Rust" → 语义检索过往记忆
架构影响
src/services/memory.py     ← 新建,封装 Mem0
    ├── remember(content)  → memory.add()
    ├── recall(query)      → memory.search()
    └── archive_session()  → 归档时批量提取

src/services/context.py    ← 新建,整合记忆 + 其他上下文
    └── build_context()    → recall() + goals + countdowns

src/prompt/engine.py       ← 简化,只负责 prompt 模板
提取策略

Mem0 内置 LLM 驱动的 fact extraction,每轮对话自动提取结构化记忆(who/what/where/when)。对于日记场景,额外规则:

  • 事实性内容:事件、决策、计划 → 提取为长期记忆
  • 情绪表达:作为元数据附着,不单独存储
  • 对话流水:不提取,已保存在 session messages 中
  • 归档摘要:标记 importance=HIGH,搜索时优先匹配

2.4 上下文注入重构 (Opt 24)

当前注入的问题:

# 现状: 只给列表,不给用法
"用户当前目标:学Rust, 每天跑步"
"即将到来的事件:培训(0天)"
"用户最近写日记的日期:2026-05-21, 2026-05-20(仅知日期,不知内容)"

LLM 收到这些后不知道该怎么做——是直接问"你今天学 Rust 了吗?"还是自然提及?没有指引。

重构策略:分级注入 + 记忆融合 + 使用指引

上下文由 ContextService 统一构建,融合 Mem0 记忆库的语义检索结果:

┌─ System Prompt ────────────────────────────┐
│ 1. 当前日期时间(已做)                     │
│ 2. 上次对话断点摘要(来自 Mem0 记忆检索)    │
│ 3. 对话节奏提示                             │
├─ Context Buffer(注入到对话开头) ──────────┤
│ 1. 相关记忆(Mem0 语义检索,≤3 条)         │
│ 2. 近期摘要(最近 3 篇,含实际内容)         │
│ 3. 活跃目标 + 进度描述                      │
│ 4. 即将到来的倒数日                         │
│ 5. 使用策略指引(告诉 LLM 如何用)          │
└─────────────────────────────────────────────┘

使用策略指引示例:

## 上下文使用策略
你有权访问以下用户信息。使用原则:
- 记忆库:历史相关事件,用于连接过往,自然提及作对话延续。
- 近期摘要:了解用户最近关注方向。
- 活跃目标:用户主动提及时可追问进展,不主动查岗。
- 倒数日:事件在 3 天内时可温和提醒,超出不提。

注入优先级:

优先级 内容 裁剪策略 来源
1 System prompt 不裁剪 prompt/engine.py
2 当前消息 不裁剪 用户输入
3 最近 20 轮对话 超出截断 session.messages
4 相关记忆(Mem0 语义检索) 最多 3 条 memory.recall()
5 上次对话断点(3 轮摘要) 超 3 轮截断 memory.search()
6 近期摘要(3 篇完整) 超 3 篇截断 storage
7 活跃目标(最多 5 个) 超 5 个截断 goal_manager
8 倒数日(最近 3 个) 超 3 个截断 countdown_manager

裁剪优先从低优先级内容开始。

2.5 Token 截断 (Opt 1)

在上下文注入重构的基础上,从最早的消息开始裁剪:

# build_messages 截断策略
MAX_HISTORY_ROUNDS = 20  # 保留最近 20 轮

history_to_send = history[-MAX_HISTORY_ROUNDS:] if len(history) > MAX_HISTORY_ROUNDS else history

2.6 日志系统 (Opt 8)

import logging

logger = logging.getLogger("ai_diary")

# 关键路径加日志,不需要全量覆盖
except Exception as e:
    logger.warning("Failed to fetch goals", exc_info=True)

在 LLM 调用、归档、Provider 切换等关键路径加上日志,输出到 ~/.ai-diary/diary.log。

2.7 搜索过滤 (Opt 16)

-- SQLite FTS5 全文索引
CREATE VIRTUAL TABLE messages_fts USING fts5(
    session_id, role, content, created_date
);

命令:/search <关键词> [-t 标签] [--from 日期] [--to 日期]

2.8 /goal add 支持类型 (Opt 5)

/goal add yearly 学 Rust
/goal add monthly 每天跑步
/goal add habit 早起
/goal add okr "Q2 上线日记 App, 3个KR"

Phase 3: 造血 —— 命令注册表落地 + 工具调用 + 测试

3.1 命令注册表实现

将 Phase 1 的蓝图落地:

# registry.py
class CommandRegistry:
    def register(self, command: Command):
        """注册一个命令,自动暴露给 /help 和 _auto_execute_commands"""
    
    def parse(self, raw: str) -> ParsedCommand:
        """解析用户输入,返回命令名 + 参数"""
    
    async def execute(self, parsed: ParsedCommand, ctx: CommandContext):
        """执行命令,ctx 包含 storage, llm_pool, session 等依赖"""
    
    def help_text(self) -> str:
        """自动生成 /help 输出"""
    
    def tool_schemas(self) -> list[dict]:
        """生成 OpenAI compatible tool definitions"""

执行时依赖注入:CommandContext 封装 storage, session_manager, llm_pool 等,通过注册表注入到每个命令的 execute() 方法。

3.2 工具调用架构 (Opt 21)

从行内命令解析升级为 OpenAI compatible tool calling:

# LLM 请求注入 tools 参数
tools = registry.tool_schemas()  # 自动从注册表生成

# LLM 返回 tool_calls 自动分发
for tool_call in response.tool_calls:
    result = await registry.execute_tool(tool_call)

保留确认机制:写入类操作在 UI 弹出 [确认执行 /goal add xxx? y/n]。

3.3 测试基础设施

tests/
├── test_storage.py      # SQLite CRUD (fixture db)
├── test_commands.py     # 命令注册表 (无 UI)
├── test_services.py     # 服务层 (mock LLM)
├── test_pool.py         # Provider failover (mock HTTP)
└── fixtures/
    └── sample_sessions.json

目标:操作层和命令层覆盖率 > 70%,TUI 层不做自动化测试。


6. 架构目标

完成后的架构

┌──────────────────────────────────────────┐
│ TUI (app.py ~250 行)                      │
│ 只做: 渲染、事件绑定、UI 状态              │
│ CSS: diary.tcss (独立文件)                 │
├──────────────────────────────────────────┤
│ Command Registry (src/commands/)          │
│ 一处定义 → 自动 help + 用户输入 + AI输出复用│
├──────────────────────────────────────────┤
│ Context Engine (src/services/context.py)  │
│ 整合 Mem0 记忆 + 目标 + 倒数日,分级注入   │
├──────────────────────────────────────────┤
│ Memory Layer (src/services/memory.py)     │
│ Mem0 封装: remember / recall / archive    │
├──────────────────────────────────────────┤
│ Service Layer (src/services/)             │
│ Session, Archive, Goal, Countdown,        │
│ Provider, Search, Doctor, Edit            │
│ 纯函数/类,无 UI 依赖,可独立测试          │
├──────────────────────────────────────────┤
│ Storage / LLM / Mem0 / Config             │
└──────────────────────────────────────────┘

各层职责边界

层 拥有什么 不拥有什么
TUI Textual widget、事件路由、通知 业务逻辑、命令执行、数据库操作
Command Registry 命令定义、参数校验、帮助生成 UI 渲染、LLM 调用
Context Engine 记忆检索调度、上下文拼接、注入优先级 prompt 文本内容
Memory Layer Mem0 CRUD、语义搜索、记忆提取规则 UI、对话管理
Service 纯逻辑、状态转换、数据验证 UI、Textual 依赖

重构原则

  1. 始终向前兼容 — 每次 PR 后 App 仍可正常启动使用
  2. 先抽离再改造 — 先建 service 层包装现有逻辑,再优化内部实现
  3. TUI 不减功能 — 重构期间不丢任何现有功能
  4. 每个 Phase 提交一次 — 不搞大爆炸式合并
  5. CLI Creator 思维 — 每个操作都是可组合的独立单元,TUI 只是消费者

附录: 文件清理记录

旧文件 处理 原因
BUGS.md 合并后删除 内容归入本文 §3
OPTIMIZATIONS.md 合并后删除 内容归入本文 §4
PLAN.md 合并后删除 内容归入本文 §5, §6
← 上一篇还是忍不住下一篇 →深夜的终端,为了一个不知所谓的东西

相关文章

  • 当提示词成了产品——一次多人协作 Prompt 设计的复盘(AI自己写的认罪书)约 12 分钟
  • xiaozhidaoyuanxing1约 27 分钟
  • 申报书填写细节(草稿)约 6 分钟

评论

需要登录账号才能发表评论。

  • 加载中…

本页内容

  • AI Diary 重构计划
  • 目录
  • 1. 项目健康评估
  • 概览
  • 功能缺失清单
  • 进度统计
  • 2. 架构诊断
  • 2.1 当前架构
  • 2.2 核心矛盾
  • 2.3 关键缺失流程
  • 2.4 架构目标
  • 3. Bug 清单
  • 3.1 已修复
  • 3.2 待修复
  • 4. 优化清单
  • 4.1 已完成
  • 4.2 部分完成
  • 4.3 未开始
  • 4.4 新增优化项
  • 5. 重构路线图
  • Phase 1: 止血 —— 架构解耦基础
  • Phase 2: 回血 —— 核心体验补齐
  • 现状: 只给列表,不给用法
  • 上下文使用策略
  • build_messages 截断策略
  • 关键路径加日志,不需要全量覆盖
  • Phase 3: 造血 —— 命令注册表落地 + 工具调用 + 测试
  • registry.py
  • LLM 请求注入 tools 参数
  • LLM 返回 tool_calls 自动分发
  • 6. 架构目标
  • 完成后的架构
  • 各层职责边界
  • 重构原则
  • 附录: 文件清理记录