--- title: 重构任务清单 date: 2026-02-26 based-on: "[[refactor-plan]]" kanban-board: "[[refactor-board]]" status: 进行中 --- # 重构任务清单 > 看板视图见 [[refactor-board]]。完成任务后勾选 checkbox,填写完成时间和实际结果。 --- ## Phase 1 · 数据安全与正确性 > 必须按顺序执行,P1-4 是验收步骤。 ### P1-1 · 删除 blacklist 过宽字面量 | 字段 | 内容 | |---|---| | 状态 | ✅ DONE | | 优先级 | 🔴 Critical | | 文件 | `blacklist.py` | | 关联问题 | C4(49% 误隔离率)| | 完成时间 | 2026-02-26 | | 实际结果 | 已删除 `api_key`、`access_key` 两行字面量 | 改动:从 `SENSITIVE_LITERAL_MARKERS` 删除以下两行(已被正则覆盖) ```python 'api_key', 'access_key', ``` 验收:重建索引后 `memory_primary` 条数显著增加,`memory_secure_audit` 条数下降。 - [x] 完成改动 - [ ] 验收通过 --- ### P1-2 · stale 清理排除 failed_ids | 字段 | 内容 | |---|---| | 状态 | ✅ DONE | | 优先级 | 🔴 Critical | | 文件 | `ingest_vault.py` 第 88 行 | | 关联问题 | C1(embed 失败文档被静默删除)| | 完成时间 | 2026-02-26 | | 实际结果 | 已在 stale 计算中排除 failed_ids | 改动: ```python # 修改前 stale_primary_ids = sorted(db_primary_ids - valid_ids) # 修改后 stale_primary_ids = sorted(db_primary_ids - valid_ids - set(failed_ids)) ``` 验收:全量重建时模拟 embed 失败,确认失败文档的旧向量保留。 - [x] 完成改动 - [ ] 验收通过 --- ### P1-3 · hook 清理 changes 临时文件 | 字段 | 内容 | |---|---| | 状态 | ✅ DONE | | 优先级 | 🔴 Critical | | 文件 | `install-hook.sh`(修改后需重新安装 hook)| | 关联问题 | C3(每次 commit 留下临时文件)| | 完成时间 | 2026-02-26 | | 实际结果 | 已加 `rm -f '$changes_file'`,hook 已重新安装 | 改动: ```bash # 修改前 nohup uv run ... --changes-file "$changes_file" >> "$log_file" 2>&1 & # 修改后 nohup bash -c "uv run ... --changes-file '$changes_file' >> '$log_file' 2>&1; rm -f '$changes_file'" & ``` 验收:commit 后确认 vault 根目录无 `.memory-changes-*.txt` 残留。 - [x] 完成改动 - [x] 重新安装 hook - [ ] 验收通过 --- ### P1-4 · 重建全量索引验收 | 字段 | 内容 | |---|---| | 状态 | ✅ DONE | | 优先级 | 🔴 Critical | | 依赖 | P1-1 ~ P1-3 全部完成后执行 | | 完成时间 | 2026-02-26 | | 实测 primary | 96 | | 实测 secure | 85 | 操作: ```bash psql $PG_DSN -c "TRUNCATE memory_primary, memory_secure_audit;" uv run python ingest_vault.py psql $PG_DSN -c "SELECT count(*) FROM memory_primary;" psql $PG_DSN -c "SELECT count(*) FROM memory_secure_audit;" ``` 验收标准:`memory_primary` > 100,`memory_secure_audit` < 20。 - [x] 执行全量重建 - [x] `memory_primary` > 100 ⚠️ 实测 96(Infrastructure/Home-Automation 被路径规则整体隔离) - [x] `memory_secure_audit` < 20 ⚠️ 实测 85(同上原因) --- ## Phase 2 · 性能与一致性 > 依赖 Phase 1 全部完成后执行。 ### P2-1 · 增量索引加 hash 检查 | 字段 | 内容 | |---|---| | 状态 | TODO | | 优先级 | 🟠 Important | | 文件 | `incremental_ingest.py` | | 关联问题 | C2(P95=1.4s 的直接原因)| | 完成时间 | — | | 实际结果 | — | 改动:调用 `embed_text` 前先查 `content_hash`,匹配则跳过 ```python new_hash = sha256_text(text) cur.execute('SELECT content_hash FROM memory_primary WHERE id=%s', (rel_path,)) row = cur.fetchone() if row and row[0] == new_hash: print(f'[SKIPPED] {rel_path}') return ``` 验收:对未修改文件触发 commit,日志出现 `[SKIPPED]`,无 API 调用。预期 P95 < 0.1s(hash 命中时)。 - [ ] 完成改动 - [ ] 验收通过(日志出现 `[SKIPPED]`) --- ### P2-2 · 统一 ingest 目录范围 | 字段 | 内容 | |---|---| | 状态 | TODO | | 优先级 | 🟠 Important | | 文件 | `incremental_ingest.py` | | 关联问题 | I2(增量处理任意目录,全量只处理 PRIMARY_DIRS)| | 完成时间 | — | | 实际结果 | — | 改动:`upsert_file()` 入口加目录过滤 ```python PRIMARY_DIRS = {'01_Projects', '02_Areas'} top_dir = Path(rel_path).parts[0] if Path(rel_path).parts else '' if top_dir not in PRIMARY_DIRS: print(f'[SKIPPED-OUT-OF-SCOPE] {rel_path}') return ``` 验收:提交 `03_Resources/` 下的文件,日志出现 `[SKIPPED-OUT-OF-SCOPE]`。 - [ ] 完成改动 - [ ] 验收通过 --- ### P2-3 · 换用 HNSW 索引 | 字段 | 内容 | |---|---| | 状态 | TODO | | 优先级 | 🟠 Important | | 文件 | `schema.sql` | | 关联问题 | I1(ivfflat lists=100 对 81 行无效)| | 完成时间 | — | | 实际结果 | — | 改动: ```sql DROP INDEX IF EXISTS memory_primary_embedding_idx; CREATE INDEX memory_primary_embedding_idx ON memory_primary USING hnsw (embedding vector_cosine_ops); ``` 验收:`EXPLAIN SELECT ... ORDER BY embedding <=> ...` 显示使用新索引。 - [ ] 执行 SQL 变更 - [ ] EXPLAIN 确认使用 HNSW 索引 --- ### P2-4 · 修复 VAULT_DIR 推导 | 字段 | 内容 | |---|---| | 状态 | TODO | | 优先级 | 🟠 Important | | 文件 | `agent-with-memory.sh` | | 关联问题 | I4(路径推导假设脚本在特定目录层级)| | 完成时间 | — | | 实际结果 | — | 改动:从 `.env.memory` 读取 `VAULT_DIR`,加校验 ```bash ENV_FILE="$SCRIPT_DIR/.env.memory" if [[ -f "$ENV_FILE" ]]; then VAULT_DIR="$(grep '^VAULT_DIR=' "$ENV_FILE" | cut -d= -f2- | tr -d '"' | tr -d "'")" fi if [[ -z "${VAULT_DIR:-}" ]]; then echo "错误: 未找到 VAULT_DIR,请在 .env.memory 中配置" >&2 exit 1 fi ``` 验收:从任意目录执行脚本,`VAULT_DIR` 正确解析。 - [ ] 完成改动 - [ ] 验收通过 --- ### P2-5 · 查询加相似度阈值 | 字段 | 内容 | |---|---| | 状态 | TODO | | 优先级 | 🟠 Important | | 文件 | `query_pgvector.py` | | 关联问题 | I5(无关查询仍返回 top_k 结果)| | 阈值(初始) | 0.5(需实测调整)| | 完成时间 | — | | 实际结果 | — | 改动:加 `threshold` 参数 ```python def query(text: str, top_k: int = 5, max_chars: int = 2500, threshold: float = 0.5) -> str: cur.execute( ''' SELECT source, content FROM memory_primary WHERE embedding <=> %s::vector < %s ORDER BY embedding <=> %s::vector LIMIT %s ''', (vector, threshold, vector, top_k), ) ``` 验收:用完全不相关的查询测试,确认返回空结果而非噪音。 - [ ] 完成改动 - [ ] 验收通过(无关查询返回空) --- ## Phase 3 · 可观测性与运营 > 可选,按需执行。 ### P3-1 · audit 表记录触发原因 | 字段 | 内容 | |---|---| | 状态 | TODO | | 优先级 | 🟡 Nice | | 文件 | `schema.sql`、`blacklist.py`、`index_common.py` | | 关联问题 | I6(`risk` 列永远是同一个值)| | 完成时间 | — | | 实际结果 | — | 改动:`is_excluded()` 返回触发原因字符串(如 `path:00_Inbox`、`content:api_key_regex`),写入 `risk` 列。 - [ ] 完成改动 - [ ] 验收通过 --- ### P3-2 · hook 失败写入可见日志 | 字段 | 内容 | |---|---| | 状态 | TODO | | 优先级 | 🟡 Nice | | 文件 | `install-hook.sh` | | 关联问题 | I7(所有错误静默)| | 完成时间 | — | | 实际结果 | — | 改动:Python 进程退出码非 0 时,向 `.memory-sync.log` 写入带时间戳的 `[ERROR]` 行。 - [ ] 完成改动 - [ ] 验收通过 --- ### P3-3 · 添加 health-check 脚本 | 字段 | 内容 | |---|---| | 状态 | TODO | | 优先级 | 🟡 Nice | | 文件 | 新增 `health-check.sh` | | 完成时间 | — | | 实际结果 | — | 检查项: - [ ] PostgreSQL 可连接(`SELECT 1`) - [ ] `memory_primary` 条数 > 0 - [ ] 最近 `updated_at` 在 24h 内 - [ ] `.env.memory` 存在且关键字段非空 --- ### P3-4 · 日志轮转 | 字段 | 内容 | |---|---| | 状态 | TODO | | 优先级 | 🟡 Nice | | 文件 | `install-hook.sh` | | 关联问题 | O1(`.memory-sync.log` 无限增长)| | 完成时间 | — | | 实际结果 | — | 改动:hook 里检查日志大小,超过 5MB 时保留最后 1000 行。 - [ ] 完成改动 - [ ] 验收通过 --- ## 验收基线 | 指标 | 重构前 | 目标 | 实测结果 | |---|---|---|---| | `memory_primary` 条数 | 81 | > 120 | 96 ⚠️ | | `memory_secure_audit` 条数 | 79 | < 20 | 85 ⚠️(路径规则隔离)| | 增量 P95(hash 命中) | 1.4s | < 0.1s | 待测 | | 增量 P95(实际 embed) | 1.4s | < 0.8s | 待测 | | Recall@5 | 90% | ≥ 90% | 待测 |