DeepTutor v1.4.8 技术解读:把 Partner 接入 My Agents 实时咨询,并为每个 Partner 建立私有长期记忆
【免费下载链接】DeepTutorDeepTutor: Lifelong Personalized Tutoring. https://deeptutor.info/.项目地址: https://gitcode.com/GitHub_Trending/dee/DeepTutor
本文基于 assets/releases/past_releases/ver1-4-8.md 官方发布说明整理与扩写,并结合当前仓库源码(
deeptutor/下 partner、subagent、builtin tools 等模块)逐条印证实现原理。
2026 年 6 月 18 日发布的 DeepTutor v1.4.8,是对 v1.4.7 的一次聚焦跟进:在My Agents中,你不仅能连接本机的 Claude Code / Codex 等本地 CLI 智能体,还能把属于自己的Partners(伙伴)当作"已连接智能体"接进来,在聊天中直接咨询它;同时,每个 Partner 从"借用主人共享记忆"升级为拥有独立私有记忆的完整主体。读完本文你将掌握:Partner 如何作为连接型智能体运行、consult_subagent与"单会话线程化"背后的调用链、partner_read / partner_memorize / partner_search三件套强制挂载的"分割记忆模型"机制,以及从 v1.4.7 平滑升级的注意事项。
版本背景:v1.4.8 解决什么问题
DeepTutor 中的 Partners 是一类具有独立人设(persona)、知识库(library)与技能(skills)的长期助手,通常通过 IM 渠道接入。在 v1.4.7 及之前,Partner 的长期记忆实际上读写的是主人的共享记忆(owner's shared memory)。
v1.4.8 做了两件关键的事情:
- 把 Partner 变成可"被咨询"的 Agent——My Agents 列表中现在与本地 Claude Code / Codex 并列展示你的 Partners,选中后整轮对话经由同一个
consult_subagent工具路由到 Partner 自己的对话回路; - 把 Partner 的记忆从主人身上"切分"出来——每个 Partner 在自己的 workspace里维护独立长期记忆,主人的共享记忆降级为只读上下文。此次变更非破坏性、零迁移:主人的记忆原样保留且仍可被读取,升级后无需任何操作。
功能一:把 Partner 作为已连接 Agent 实时咨询
1.1 使用方式
在My Agents管理界面中,Partner 与本地 Claude Code / Codex 并列展示。选择一个 Partner 连接后,在 composer(输入框)中选中它发起提问,本轮对话会以单个consult_subagent工具调用的形式,把问题转发给该 Partner自己的 chat loop——也就是说,是 Partner 以自己的人格、知识库和技能来回答,而非 DeepTutor 主模型代答。Partner 的逐步运行过程(trace)会在侧边栏实时流式展示。
从产品行为看,一个 DeepTutor 聊天会话内对该 Partner 的多次咨询会被线程化进同一个 Partner 会话,并以你的第一个问题为会话标题,归档到该 Partner 自己的历史记录中;这条连接还佩戴着 Partner 自己的头像,方便在多连接中区分。
1.2 源码级原理:consult_subagent 的调用链
consult_subagent由 deeptutor/capabilities/subagent/tools.py 中的ConsultSubagentTool实现,其工具描述明确说明它可以问两类对象:用户本机的本地智能体 CLI(Claude Code、Codex、Gemini CLI、Kimi CLI、opencode、MiMo Code 等),以及用户的 Partners——Partner 用"自己的 persona、library 与 skills"来回答。
关键设计:该工具只暴露一个question参数给模型,其余服务端信息全部由能力层的augment_kwargs注入_subagentspec(见 deeptutor/capabilities/subagent/capability.py):
updated["_subagent"] = { "kind": conn["kind"], "cwd": conn.get("cwd") or "", "partner_id": conn.get("partner_id") or "", # Partner 连接时携带 partner_id "name": conn["name"], "budget": _resolve_budget(context), # 本轮咨询预算(用户配置的 max rounds) "config": config, "state": state, # 轮内状态:咨询次数 / 会话 id "images": images, "session_key": skey, }其中partner_id非空即代表后端类型为 Partner(PARTNER_BACKEND_KIND)。当目标为 Partner 时,系统提示词(system prompt)会向主模型描述 Partner 的"同事"定位——例如英文文案为"a companion with its own persona, library and skills … Your exchange is archived as one complete session in that partner's history, and its reply is shown to the user live"(capability.py),中文场景对应"把它当作一位独立的同事来求助"。
执行期还包含以下可验证机制:
- 咨询预算:同一轮内模型最多咨询
budget次(范围由CONSULT_BUDGET_MIN/MAX约束,可被 composer 中的subagent_consult_budget覆盖);达到上限后工具会返回"Consult budget reached"并明确要求模型直接作答。 - 会话连续性:后端的 session/thread id 会写入 deeptutor/services/subagent/sessions.py 维护的
subagent_sessions.json,按"聊天会话 + 连接"复合键记忆。因此同一次 DeepTutor 聊天内的多次咨询复用同一个 Partner 会话,Partner 能记住此前的上下文进行层层追问;甚至跨轮次也能恢复同一 live 会话(连接断开时才清理对应条目)。 - 实时 trace:所有后端原生事件经
on_event汇聚后以subagent_event单一 trace_kind 流式推送到侧边栏,用consult_index与subagent_merge_id区分不同轮次的增量内容,形成"CLI 忠实"的对话式转写。 - 归档与标题:整段往来作为一个完整会话归档进 Partner 的历史,标题取自你的第一个问题(与上面注入的 system framing 相呼应)。
- 图片转发:仅当后端配置开启
forward_images时,本轮图片附件才被物化到临时目录供 Partner 摄取,结束后清理。
功能二:Partner 拥有自己的记忆(分割记忆模型)
2.1 三个 always-on 工具
此前 Partner 在对话中直接使用聊天的read_memory/write_memory,读写的是主人的共享记忆。v1.4.8 起,每个 Partner 轮次都用下面三个**常驻(强制挂载、不可关闭)**工具替代它们(实现在 deeptutor/tools/partner_memory.py,元组PARTNER_BUILTIN_TOOL_NAMES是它的单一事实来源):
| 工具 | 职责 | 关键参数 | 权限边界 |
|---|---|---|---|
partner_read | 读取"主人的共享长期记忆+Partner 自己的记忆",两段拼接返回 | 无 | 主人层只读;自己的层可被partner_memorize维护 |
partner_memorize | 把一条值得记住的偏好/事实写入Partner 自己的workspace | op(add/edit,必填)、text(≤240 字符,必填)、target_id(edit时的m_xxx条目 id)、reason(可选,写入记忆日志) | 永远只写自己的记忆,碰不到主人的记忆 |
partner_search | 在 Partner 自己全部历史会话上做关键词检索 | query(必填,忽略大小写)、limit(默认 30,最大 100) | 只检索自己的会话 |
三个工具的返回也都携带结构化元数据,例如partner_read返回has_shared/has_own标记,partner_search按时间倒序返回形如[会话标题 · role · 时间戳] 命中片段的摘要行。
2.2 数据落在哪里
从 partner_memory.py 模块 docstring 与实现可见,Partner 的长期记忆保存在Partner 的合成 workspace:data/partners/<id>/workspace/memory。partner_memorize写入时会先以"partner"事件源向记忆 trace 提交一条preference_stated事件,再调用store.write_preference()落盘,保证 trace 与偏好位于同一棵记忆树内、脚注引用可解析。
partner_read的实现同时做了两次带路径覆盖的读取(细节见源码):
with memory_path_service_override(get_admin_path_service()): # 主人 L3 层(只读) shared = _concat_l3() with memory_path_service_override(get_current_path_service()): # Partner 自己 L3 层 own = _concat_l3()输出固定为两个小节:## Shared memory (the owner's — read-only)与## Your own memory,空层会明确标注(如"none yet — use partner_memorize to add")。
Partner 用户身份本身由 deeptutor/services/partners/scope.py 界定:PARTNER_USER_PREFIX = "partner_",即 Partner 的 user_id 为partner_<id>,并映射到其独立 workspace 与 scope——这正是"路径覆盖读取能区分主人与 Partner"的根基。
2.3 强制挂载与开关移除的实现
在 deeptutor/agents/chat/agentic_pipeline.py 的工具组合阶段,能直接看到这条"分割记忆模型"的落地代码:
# Partners get the partner_* memory/history tools force-mounted and # chat's read_memory/write_memory suppressed — the split-memory model forced=PARTNER_BUILTIN_TOOL_NAMES if is_partner else (), suppressed=_PARTNER_SUPPRESSED_TOOLS if is_partner else (),其中_PARTNER_SUPPRESSED_TOOLS = ("read_memory", "write_memory")。也就是说:只要这一轮是 Partner 轮次(_is_partner_turn判定),partner_read / partner_memorize / partner_search就被强制加入工具面,同时聊天记忆工具read_memory/write_memory被压制。
同时,在 deeptutor/tools/builtin/init.py 中三个 Partner 工具被"全局注册以保证 schema/API 稳定,但绝不在产品聊天中挂载",并刻意缺席于CONFIGURABLE_BUILTIN_TOOL_NAMES——它们的注释写明:"they are mandatory, not owner-configurable"。这就是发布说明所述"per-Partner 的 read_memory / write_memory 开关从 Partner 工具设置中移除"的源码依据:既然 Partner 的记忆访问已由三件套接管且不可配置,旧的读写开关自然失去意义。
2.4 非破坏性与权限模型
此次变更是自动且非破坏性的:
- 已存在的 Partner 依然能读取主人的共享记忆(作为上下文),只是新增的记忆落到 Partner 自己的可写存储;
- 主人的记忆数据完全不被触碰,保持可读;
- Partner 永远无法写入或篡改主人的记忆——写路径被
partner_memorize硬编码到自己的 workspace。
一个值得注意的配合:Partner 是"把主人的 exec/工具白名单按伙伴逐个允许/拒绝"的,而CONFIGURABLE_BUILTIN_TOOL_NAMES中read_memory/write_memory等 15 个工具仍可按伙伴粒度控制;Partner 记忆三件套则不在其中,属于强制面。发布说明中强调的"可选择性拒绝(例如面向 IM 的 Partner 可被禁止内存访问)"仍然针对的是旧聊天工具面,不会与三件套冲突。
功能三:将 Partner 对话作为上下文引用(仅管理员)
v1.4.8 还允许把一段 Partner 会话拉取进 DeepTutor 聊天,作为**引用转写(referenced transcript)**上下文——仅管理员(admin-only)可用,并以"第三方"身份冠以 Partner 自己的名字呈现,与导入的智能体外史(imported agent histories)的处理方式相同。
这与会话级"参考转写"能力是同一套机制:在 deeptutor/services/session/source_inventory.py 中存在serialize_referenced_transcript(meta, messages, language=...),用于把一段历史消息按引用来源序列化为转写块;该函数也被 deeptutor/services/session/turn_runtime.py 在回合运行时调用(L1435/L1451 附近)。Partner 会话作为引用来源时,其"来源身份"就是 Partner 自己的名字,从而在回答上下文中明确这是一位第三方的独立输出。
升级说明与验证
v1.4.8 是 v1.4.7 的直接后继,升级路径与官方声明一致:
- pip 用户:
pip install -U deeptutor; - Docker 用户:拉取
ghcr.io/hkuds/deeptutor:latest; - 无需迁移(No migrations),记忆变更自动生效且非破坏性。
升级后可以这样验证新行为:
- 打开 My Agents,确认 Partners 已与本地 Claude Code / Codex 并列;连接某个 Partner 并在 composer 中选择它,观察侧边栏是否实时流式出现该 Partner 自己的运行 trace。
- 向该 Partner 连续追问多次,然后在 Partner 的历史记录中确认这些咨询被归档为一个以首个问题为标题的完整会话。
- 在 Partner 会话中让其记住一条偏好,再回到主聊天用
partner_read语义验证:主人共享记忆未被改动,而 Partner 自己的记忆里新增了该偏好(数据文件位于data/partners/<id>/workspace/memory)。 - 检查 Partner 工具设置:旧的
read_memory/write_memory开关应已消失(三件套强制且不可配置)。
延伸阅读(源码索引)
若想继续深入这套机制的实现,可在当前仓库中按以下路径阅读:
- 发布说明原文:assets/releases/past_releases/ver1-4-8.md,上一版本参考 ver1-4-7;
- Partner 记忆三件套实现:deeptutor/tools/partner_memory.py;
- 工具面组装与强制/压制逻辑:deeptutor/agents/chat/agentic_pipeline.py;
- Partner 工具全局注册与"不可配置"声明:deeptutor/tools/builtin/init.py;
- 咨询工具与后端调用:deeptutor/capabilities/subagent/tools.py、deeptutor/capabilities/subagent/capability.py;
- 会话连续性与归档:deeptutor/services/subagent/sessions.py、deeptutor/services/partners/sessions.py(含
PartnerSessionStore,被partner_search用于检索全部历史); - Partner 用户作用域与路径映射:deeptutor/services/partners/scope.py、deeptutor/multi_user/paths.py。
小结:v1.4.8 表面上是一份小型跟进发布,实质完成了一次清晰的权限与职责重构——Partner 从"借用主人记忆的聊天参与者"升级为"有独立记忆、可被主聊天实时咨询的完整 Agent"。理解consult_subagent的注入式 spec、forced/suppressed工具面机制与partner_*三件套的数据落盘边界,就掌握了这次发布的技术内核。
【免费下载链接】DeepTutorDeepTutor: Lifelong Personalized Tutoring. https://deeptutor.info/.项目地址: https://gitcode.com/GitHub_Trending/dee/DeepTutor
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考