Skip to content

fix(schedule): 冷段细化贴段首,避免窗口被夹到段尾 - #7

Merged
AmriaLin merged 3 commits into
mainfrom
fix/refine-window-target
Oct 21, 2026
Merged

AmriaLin merged 3 commits into
mainfrom
fix/refine-window-target

Conversation

@AmriaLin

@AmriaLin AmriaLin commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

概要

修 IdlePlanner._refine 里的一处窗口起点夹取错误,顺带把 README 里跟当前实现不符的描述改掉并精简,requires-python 下限对齐 3.11。

调度修正

_refine 先用 seg_start.replace(hour=target_hour) 算偏好时刻,段首晚于偏好时刻时
target += timedelta(days=1),于是 target 落到段外,随后被
latest_start = max(f_start, f_end - need*60) 夹到段尾 —— 整段空闲只用到最后那点。

复现:只让 00:00–04:00 与 09:00–24:00 活跃,冷段是 04:00–08:00 一整块。
原实现给出 07:35–07:45(10 分钟),改后给出 04:00–07:45(225 分钟)。

影响不只在窗口长度:07:35 起跑意味着 DMN 顶在用户起床前,尾部 buffer 刚好被吃掉,
一旦超时就撞上用户第一句话,而这块本来有四个小时可以安排。

修法只有一行,if target < seg_start: target = seg_start。

验证

  • 300 组随机作息 × 随机 now × 随机 penalty:窗口非空、起点不在过去、长度 ≥ 1、
    非强制时不落进活跃时段,零失败
  • 冷段内插入 04:10 消息 → 窗口绕开,重叠 0 条;凌晨连续活跃 → 02/04/06 槽被判冷,03:00 不再优先
  • 时区名写错 → 退回 UTC+08:00 不抛异常;换时区/换 uid → 旧热度图作废(536.2 → 0)
  • merge_noise 与二分结果对照暴力解,5 组随机样本一致
  • 30 天、每天 120 条的密度下,200 次规划平均 3.2 ms
  • ruff check / ruff format --check 通过,全量 py_compile 通过

文档

README 从 766 行压到 727 行,改动以删除和纠错为主:

  • Python 版本改回 3.11+(此前误写成 3.10–3.13)
  • L3 触发改为空闲窗口规划,原来只有指数退避公式
  • per_session_memory_limit 默认值 50 改成实际的 100
  • 惩罚计数器并非不持久化,改为落在 SubconsciousState.payload
  • 巩固循环工具 20 → 21,补 subconscious_get_context
  • 提示词变量改为 current_date,不再是 last_run / total_tokens 那套
  • 删掉与代码不符的 workflow 管线图、与 .env 表重复的嵌入配置表、
    三层模型的大图(改成三句话)、配置表里 18 行 idle 逐项(改成指向 SubconsciousConfig)

依赖

requires-python 从 >=3.10 提到 >=3.11:chromadb 1.5.9 自身虽然写的是 >=3.9,
但它拉进来的 numpy 2.4 / onnxruntime 1.30 / sqlalchemy 2.1 都是 >=3.11,
声明 3.10 会在解析阶段就失败。uv.lock 随之重解,移除 py3.10 分支(-782 行)。

[tool.ruff] target-version 保持 py310 未动 —— 提到 py311 会让 UP017 对
timezone.utc 批量报错,那是另一件事,不该混进来。

Review 关注

  1. 窗口起点逻辑改的是"偏好时刻在段外"这一支,偏好时刻本就在段内的路径输出不变,
    有对应用例覆盖
  2. uv.lock 变动较大但都是 py3.10 分支清理,185 个包与本地环境一致
  3. README 只做减法,没有新增章节;若还想留完整的 idle 参数表,可以再商量

Summary by Sourcery

修正冷段窗口起点规划并同步项目文档与 Python 依赖版本要求。

Bug Fixes:

  • 修正空闲窗口规划在偏好时刻落于冷段之外时错误夹到段尾的问题,确保优先使用完整可用的冷段。

Enhancements:

  • 精简并校正文档,使其与当前的空闲窗口调度、记忆管理和巩固循环行为保持一致。

Build:

  • 将项目支持的最低 Python 版本提升至 3.11,并同步更新锁文件依赖解析结果。

Documentation:

  • 更新 README 中的调度机制、配置默认值、工具、提示词变量、命令和开发说明。

_refine 里 target 由 seg_start 替换 hour 得到,段首晚于偏好时刻时
target 加一天会落到段外,再被 latest_start 夹到 f_end-need,
整段空闲只用到最后 run_estimate。改为 target = seg_start。

docs: README 去掉与当前实现不符的描述并精简,pyproject 下限对齐 3.11
@sourcery-ai

sourcery-ai Bot commented Oct 21, 2026 •

Copy link
Copy Markdown

Reviewer's Guide

本 PR 修复了空闲窗口细化在偏好时刻早于冷段段首时的起点计算错误,使调度能够利用完整冷段而非退化为段尾短窗口;同时纠正文档与当前空闲窗口实现的偏差,并将项目 Python 支持下限和锁定依赖统一到 3.11+。

Flow diagram for corrected idle-window refinement

flowchart TD
    A["冷段开始 seg_start"] --> B["计算偏好时刻 target"]
    B --> C{"target < seg_start"}
    C -->|是| D["target = seg_start"]
    C -->|否| E["保留 target"]
    D --> F["计算 latest_start"]
    E --> F
    F --> G["计算 start_ts"]
    G --> H["利用冷段并预留 buffer 与运行时间"]
Loading

File-Level Changes

Change Details Files
修正冷段细化时偏好时刻落在段首之前的起点夹取逻辑,避免目标时间被错误推到次日并最终夹到段尾。
  • 将段外的偏好时刻直接贴到当前空闲段首,而不是加一天
  • 保留偏好时刻已在空闲段内时的原有输出路径
  • 通过随机作息、消息插入、边界和性能验证覆盖调度行为
amrita_plugin_memory/rethinking/schedule.py
同步更新项目文档,使调度机制、持久化状态、工具、配置和运行流程符合当前实现。
  • 将 L3 描述改为空闲窗口触发,并补充热度图、冷段、细化和 penalty 语义
  • 更新工具数量、上下文工具、提示词变量、状态持久化和命令说明
  • 删除过时的流程图及冗余配置表,修正默认值和模块结构说明
README.md
提高项目 Python 运行时下限并重新生成锁文件以移除 Python 3.10 分支。
  • 将 requires-python 从 >=3.10 调整为 >=3.11
  • 重新解析 uv.lock,清理仅适用于 Python 3.10 的依赖分支
  • 保持 Ruff target-version 不变,避免引入无关代码风格迁移
pyproject.toml
uv.lock

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 3 issues

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="amrita_plugin_memory/rethinking/schedule.py" line_range="533" />
<code_context>
         )
         if target < seg_start:
-            target += timedelta(days=1)
+            target = seg_start
         # 起点尽量靠近偏好时刻,但必须落在空闲段内且留够运行空间
         latest_start = max(f_start, f_end - need * 60)
</code_context>
<issue_to_address>
**Preferred run starts too early**

When a cold segment crosses midnight and the preferred hour falls after midnight within that segment, `_refine` assigns `seg_start` to `target`, so `start_ts` schedules the run from the segment head (for example, 22:00) instead of the preferred time (03:00), even though that time falls within the segment.

Keep the next-day preferred target when it falls within the cold segment instead of unconditionally assigning `seg_start`.
</issue_to_address>

### Comment 2
<location path="README.md" line_range="498" />
<code_context>
-| 代码质量   | Ruff + Pyright                                     |
+| 组件       | 技术                                                        |
+| ---------- | ----------------------------------------------------------- |
+| 运行时     | Python 3.11+ / NoneBot2 / AmritaCore                        |
+| 向量库     | ChromaDB(PersistentClient 或 HttpClient)                  |
+| 嵌入       | OpenAI Embedding / Ollama Embedding                         |
</code_context>
<issue_to_address>
**Installation rejects stated Python versions**

When a user follows the README runtime requirement on Python 3.14 or later, the README says Python 3.11+, but `pyproject.toml` limits `requires-python` to `<3.14`, so package installation rejects a runtime the README says is supported.

Make the README runtime requirement match `pyproject.toml`, or update the package metadata to support the versions stated in the README.
</issue_to_address>

### Comment 3
<location path="README.md" line_range="573" />
<code_context>
 | `decay.sweep_interval_hours` | `24`    | 分层清理周期(小时)                        |
 | `decay.sweep_batch_limit`    | `5000`  | 单次清理最多扫描的记忆条数                  |
-| `per_session_memory_limit`   | `50`    | 单个分区(uni_id)的记忆条数上限            |
+| `per_session_memory_limit`   | `100`   | 单个分区(uni_id)的记忆条数上限            |
 | `memo_max_chars`             | `1000`  | L1 备忘录字数硬上限                         |
-| `enabled`                    | `false` | 是否启用离线巩固循环                        |
</code_context>
<issue_to_address>
**Config metadata still says 50**

When a consumer displays the `ConfigFile` field description, `ConfigFile.per_session_memory_limit` has a default of 100, but its Pydantic field description still says the default is 50; consumers that display the config metadata are given a value that conflicts with the README and runtime default.

Update the field description to say the default is 100.
</issue_to_address>

Sourcery assessment

Needs a human reviewer. 2 findings to address first, and when the preferred hour falls before a selected idle segment, the scheduler will now start at the segment’s beginning instead of clamping the run toward its end; if that timing is wrong, the agent could send a user-facing message earlier than intended. Reverting restores the prior scheduling behavior, but cannot retract a message already sent.

Blocking findings: amrita_plugin_memory/rethinking/schedule.py:533, README.md:498


Sourcery is free for open source - if you like our reviews please consider sharing them ✨

Comment thread amrita_plugin_memory/rethinking/schedule.py Outdated
Comment thread README.md Outdated
Comment thread README.md
Sourcery review 指出 target=seg_start 会把 22:00-09:00 这种跨午夜段的起点
拉到段首 18:00/22:00,而 03:00 其实就在段内。改为次日偏好时刻仍在段内
(nxt < seg_end)时取 nxt,两次都不在段内才退到段首。

顺带修正 config 里 per_session_memory_limit 的描述(写的是 50,实际 100),
README 的 Python 版本改成 3.11-3.13 与 requires-python 的 <3.14 对齐。
@AmriaLin

Copy link
Copy Markdown
Contributor Author

三条都处理了,push 在 5c3a549。

Comment 1 — 跨午夜冷段起点被贴到段首:这条是对的,而且比我原来那个修改更严重。我复现了:只让 09:00–18:00 活跃,冷段是 18:00 到次日 09:00,原实现给出 18:00–07:45,825 分钟 —— 起点贴在段首,而 03:00 明明就在段内。问题在于我把「偏好时刻早于段首」一律当成「偏好时刻在段外」,跨午夜时次日那次其实还在段内。

修法不是回退成 target += timedelta(days=1)(那会把非跨午夜的情况推到段外,再被 latest_start 夹到段尾,就是我最初要修的那个 bug),而是先判断次日偏好时刻是否仍在段内:

if target < seg_start:
    nxt = target + timedelta(days=1)
    target = nxt if nxt < seg_end else seg_start

三种情况都验过:跨午夜且 03:00 在段内 → 起点 03:00;冷段 04:00–08:00 且偏好时刻在段外 → 起点 04:00 用满 225 分钟;段内且不跨午夜 → 03:00 不变。Sourcery 提到的「可能比预期更早给用户发消息」正是修掉的行为,现在不会了。

Comment 2 — README 说 3.11+ 但 metadata 是 <3.14:改成 Python 3.11–3.13,两处都改了。

Comment 3 — ConfigFile 的字段描述写 50:改成 100。这个是我改文档时只改了 README、漏了代码里的 description,确实会误导看配置元信息的人。

回归验证:300 组随机不变量仍零失败,二分与暴力解一致,30 天密度下 200 次规划平均 3.1 ms,ruff 与 py_compile 干净。

@AmriaLin
AmriaLin merged commit a9546b9 into main Oct 21, 2026
3 checks passed
@AmriaLin
AmriaLin deleted the fix/refine-window-target branch October 21, 2026 13:34
@AmriaLin AmriaLin mentioned this pull request Oct 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant