Merge single ongoing agent conversations into main

This commit is contained in:
brother7 committed 2026-09-28 14:35:49 +08:00
commit 2b4b7710d1
32 files changed
+1442 -149

No files matched your search

@@ -1,6 +1,6 @@
# ADR: 官方编程老师与项目会话导航
- Status: Accepted / implemented, paged reads amended 2026-09-27
- Status: Accepted / implemented, single ongoing chat amended 2026-09-28
- Date: 2026-09-22
- Approval: 用户确认项目默认 Agent、项目下会话、多老师列表与移除试聊;随后确认使用 Yuxi 原生智能体、客户端提供三个只读工具、沿用学生账号付费,并要求实施及合并。2026-09-24 明确取消客户端固定老师/朋友分类,统一使用后端下发智能体,并要求合并上下文预算修复。
@@ -10,17 +10,19 @@
## Decision
- 2026-09-28 用户确认像微信联系人一样,每个账号与每个下发智能体只有一个持续聊天,并授权实施与合入主分支。以稳定 config_id 标识智能体,不按名称、默认标记、项目或版本创建第二个可见聊天;本次范围为本机持久化,不含跨设备同步。
- 新项目创建内部默认编程 Agent 并保存 defaultAgentId,会话直接列在项目下。打开项目不自动创建空会话,首次发送或显式新建才创建;历史 Agent ID、配置和 Pi 会话绑定保留。
- Yuxi 拥有老师提示词、模型和所选 Skills、知识库、MCP、子智能体。Works Square 运营菜单首先展示下发列表,添加时从 Yuxi 选择并发布当前配置,支持多位、默认选择与启停,不维护第二份提示词编辑器或试聊界面。
- 运营显式同步 Yuxi 配置产生不可变下发版本。新话题可选择已下发老师,默认使用运营指定老师;已有话题固定原版本。配置与资源绑定固定,Skill 按问题建立运行快照,知识内容和远端工具仍由各自服务拥有;资源撤销可能阻止执行,版本不表示复制全部外部资源。
- 运营显式同步 Yuxi 配置产生不可变下发版本。每次接受新问题使用当前已下发版本,运行中的问题保持原版本;版本变化不另建可见聊天。配置与资源绑定固定,Skill 按问题建立运行快照,知识内容和远端工具仍由各自服务拥有;资源撤销可能阻止执行,版本不表示复制全部外部资源。
- Yuxi 原生 Request/Run、PostgreSQL checkpoint 和 Redis 事件拥有云执行。Main 冻结本次账号、项目、来源 Pi 已完成分支的公开消息及老师话题,通过三个只读工具提供目录、UTF-8 文件行段和会话原文。文件按读取时内容提供,会话按本轮快照读取,不上传完整工程、Pi 原始日志或思考。
- 本地工具只交给老师主线程:云端持久中断完整批次,Main 主动回传配对结果,关联 Run 续接同一问题。用户于 2026-09-27 授权改造读取工具:声明 read_protocol=2 的客户端支持按层目录与连续原文分页,每问题最多 12 批、成功结果累计 64 KiB、单页 8 KiB,剩余不足 512 字节时收尾;旧客户端保持六批。最后结果消费后显式禁止继续选工具并要求基于已有证据回答。保留原付款人、版本、上下文和截止时间;读取限当前项目,允许 .makelore/project.json,排除其余内部记录与 Git 数据,不提供本地写入或命令。
- Works Square 验证学生编程资格,使用专用短期老师凭据;聊天模型费用记入学生 ai_programming / coding_teacher 账本。老师资源仍归 Yuxi 创建者,个人 Agents 模块继续创建者付费,老师问题不占创建者个人智能体金额上限。
- 云端保存老师消息、执行和收到的片段;Main 按账号、项目和来源会话保存话题索引与展示历史。旧本地老师话题保留原路径。停用阻止后续调用,历史仍可查看;断线按游标和持久状态恢复,重启后中断旧问题,下次提问先停止旧问题再绑定新上下文。
- Code 咨询统一使用服务端下发智能体,不内置老师/朋友角色、本地教学人设或按名称分配的工具权限。名称、头像、简介、欢迎语和推荐问题均来自发布定义;新话题使用用户选择或运营默认项,已有话题继续固定原定义和版本。所有下发项共享上述只读工具边界,通用讨论组件协议不定义智能体人格。
- 右侧咨询保留已实现的结构化讨论和主动跟进:自动检查在隐藏窗口、操作会话运行、来源归档、智能体回复或咨询草稿编辑期间延后,Main 再校验来源、启停和冷却。跟进使用同一发布定义与学生计费,不伪造学生消息。正文、保存历史和带回草稿仅接收云端主线程文本;子线程事件仍推进续传游标。带回回答只追加草稿,不自动发送。Code/Pi 执行和项目 Plugin 生命周期保持各自边界。
- 所有下发智能体以头像和名称在顶栏并排展示,点击直接切换,移除面板内的新话题智能体下拉框;数量多时在顶栏内横向滚动,不挤占窗口控件。切换恢复所选智能体的历史或空输入框,不创建话题、不发起模型调用;新话题使用所选项的当前发布版本,旧话题仍固定原版本。历史菜单保留,查看其他智能体的话题时同步顶栏选中项。
- 当前咨询历史按账号、项目保存,每轮绑定当时的操作会话;未发送草稿按账号、项目及服务端智能体 ID 分别保存。旧项目级未归属草稿折叠保留供查看,不猜测归属或自动填入其他智能体。旧客户端生成的朋友话题及草稿只读保留,不再继续调用,也不自动绑定云端智能体;旧来源会话级历史不自动迁移。现有云端 `coding-teacher` 契约与存储名保留为实现兼容,不构成角色分类。
- 云端保存执行线程、消息和收到的片段。Main 在 userData/agent-conversations/<account>/<agent> 保存轻量聊天索引和独立轮文件;每页50轮,流事件只更新当前轮。轮文件先于索引原子保存,恢复不重发模型。停用阻止后续调用,历史仍可查看;重启将未完成问题标为中断,下次提问先停止旧问题。
- 每轮冻结项目、Pi 来源、发布版本及只读范围。相同项目/来源/版本沿用内部 Yuxi 线程,任一改变都开启内部执行段,携带有预算的公开近期交流;可见聊天保持连续。历史消息可按 ID 读取,读到另一项目的旧交流不授予其文件权限。Pi 与项目配置仍归项目所有。
- Code 咨询统一使用服务端下发智能体,不内置老师/朋友角色、本地教学人设或按名称分配的工具权限。名称、头像、简介、欢迎语和推荐问题来自发布定义。所有下发项共享上述只读工具边界,通用讨论组件协议不定义智能体人格。
- 结构化讨论在聊天内按项目保存,历史轮保留原卡片快照。正文、历史和带回草稿仅接收云端主线程文本;子线程事件仍推进续传游标。带回回答只追加原项目/Pi 来源的草稿,不自动发送或改写当前其他来源。已下发智能体的旧 Renderer 定时跟进停止派发;新的 Main 主动观察由独立任务实施,本次未接入建议投递,后续必须并入同一聊天,不能恢复项目话题入口。
- 所有下发智能体以头像和名称在顶栏并排展示,溢出横向滚动。点击恢复唯一聊天或空态、独立草稿和未读,不调用模型;移除新话题加号和话题下拉框。旧原始记录保留单独只读入口。最近50轮先加载,历史分页与流事件按请求身份合并;切换智能体保留窗口内阅读位置,已读位置由 Main 持久保存。
- 未发送文字草稿按账号/智能体保存;明确引用仍携带项目/Pi 来源,来源切换后须移除旧引用或恢复原上下文才能发送。只导入已登记项目中能证明当前账号与 config_id 归属的旧话题,以项目/话题/请求来源身份去重,原文件保留;重复正文不去重,失联目录稍后重试。无法证明归属的朋友/旧老师历史及旧草稿只读保留,不猜测身份。移除项目不删除账号级聊天;现有云端 coding-teacher 名称不构成角色分类。
- 输入预算在编译时计入完整执行请求。本地旧模型路径按约 2 UTF-8 字节估算 1 Token,工具定义、调用和读取预留纳入同一计量;估算不替代模型上限或实际 usage。云端以完整 JSON 转义后的 query 字节数裁剪来源节选,原文仍可按 ID 读取。固定配置/当前讨论超限与用户问题/引用超限分别提示,不静默修改发布预算、问题、明确引用或话题版本。
- 咨询回答以 Markdown 渲染标题、列表、表格、代码、HTTP(S) 链接/图片和公式,代码与表格在栏内滚动;普通 JSON、Markdown 链接与代码中的字面转义保持原义。discussion-v1 仅由顶层 reply/quickReplies/tool 或专用围栏识别,组件仍遵循既有生命周期。解析失败保留原讨论卡与可恢复正文,完整 unparsedResponse 单独持久保存并默认折叠,不再次注入后续会话上下文。
@@ -28,6 +30,7 @@
## Evidence And Release Boundary
- 2026-09-28 单会话源 `7aa81da616449dead25557476cf4f1433edf84b5` 已纳入本地 main。源365项相关单测、Renderer typecheck、变更文件 lint、Vite构建和真实 Electron 夹具交互通过;扩展 Main 检查与基线同为66项既有诊断。合并保持产品/测试与源一致,不重跑相同验证。未推送、打包、部署或进行真实付费模型验收;主动观察组合验收另行完成。见[源任务](../30-worklog/tasks/20260928-agent-single-chat-27da516b.md)及[集成记录](../30-worklog/tasks/20260928-merge-agent-single-chat-9911c6df.md)。下方旧话题交互证据仅记录当时版本。
- 2026-09-24 用户明确要求多智能体在顶栏铺开并授权合并,客户端源 `a10cf000157a1a3edcfad43705e0824f19568a33` 已快进纳入本地 `main`。源相关单测、17 项浏览器布局和 1 项 Electron 夹具交互、类型/lint/构建通过,具体分批验证见[源记录](../30-worklog/tasks/20260924-consultation-topbar-22717e99.md)。合并未改变已测产品字节;不代表已更新安装版或真实供应商验收。
- 2026-09-24 渲染源 `97837cef90baf6bb02cfda0cef7b938081f3c5f7` 与 Yuxi 配套源 `02f27f33a8b816589036503f0720d446418c28aa` 已纳入本地主分支集成。客户端源 287 项单测、12 项真实浏览器布局用例及独立审查通过;Yuxi 82 项相关协议回归与真实 LangGraph 确定性模型测试通过。浏览器接口为替身,不代表安装版或线上模型已验收;旧版已覆盖的失败原文无法恢复。
+1 -1
View File
@@ -2,7 +2,7 @@
- 2026-09-26 已确认[老师 Agent 产品定位](../00-brief/project-positioning.md#老师-agent-的产品定位):某一领域杰出人物的能力转化而成的智能体,供平台学生使用;蒸馏属于实现方法,以真实案例验证领域能力;补充下方既有咨询技术决定。
- [下发智能体咨询与项目会话导航](ADR-2026-09-22-coding-teacher.md):2026-09-24 amended / implemented;取消固定老师/朋友分类,运营下发 Yuxi 智能体并指定默认,发布定义拥有人设与显示资料,Main 提供一致的项目/会话只读工具。旧合成朋友话题与草稿只读保留;学生计费、版本固定与主线程正文路由保持,输入预算区分近似 Token 与精确 JSON 字节。
- [下发智能体咨询与项目会话导航](ADR-2026-09-22-coding-teacher.md):2026-09-28 amended / implemented;每个账号与下发智能体使用唯一持续聊天,跨项目保留历史,移除新话题和话题选择。项目/Pi 来源/发布版本在每轮接受时冻结,变化时只轮转内部线程。Main 持久化分页历史与已读,草稿按账号/智能体隔离;可证明归属的旧话题导入且原文件保留,其余只读。学生计费、统一只读工具和主线程正文保持;主动观察建议接入由独立任务继续。
## Active Decisions
@@ -0,0 +1,147 @@
# 用户与下发智能体的单一持续会话
- 日期:2026-09-28
- 状态:待采纳设计;尚未修改产品代码或已接受 ADR。
- 任务:20260928-agent-single-chat-27da516b
- 代码基线:b26e25c9ed26bf30f4d0bc03869c223f0fe34386
- 用户要求:像微信联系人聊天,一个用户与一个智能体只有一个可见会话。
- 范围解释:按用户+智能体理解为跨项目连续聊天;先保持现有本机历史持久化范围,不承诺跨设备同步。
## 1. 用户体验
顶栏继续显示所有已下发智能体。点头像直接恢复这个用户与该智能体的唯一聊天、草稿、未读和阅读位置。移除学生面板的“新话题”加号和“以往讨论/选择话题”下拉框。向上滚动加载更早消息。
切换项目或 Pi 会话不另建可见会话。输入区显示本轮上下文,例如“当前项目:天气 App · 操作对话:我要做个天气 App”;上下文变化时在下一轮提问旁显示来源。已开始的问题仍绑定原项目,切页不会改走它的文件读取路径。
不同智能体各有自己的聊天;昵称、头像、默认项、发布版本变化不会变成另一个联系人。保留已有 Markdown、工具活动和结构化讨论渲染,不顺带增加附件、搜索或聊天删除系统。
## 2. 已确认现状与改造位置
| 源码 | 已确认事实 | 影响 |
| --- | --- | --- |
| electron/coding-teacher/service.ts | scopedStore 在项目 .makelore/teacher-conversations/账号/范围下存历史;create 每次生成 UUID;topic 固定项目和版本 | 会话身份与本轮上下文分离 |
| shared/coding-teacher.ts | TeacherTopic 持有 projectId、version、definition;TeacherRequest 只有可选 sourceConversationId | 项目与发布版本下沉到请求 |
| electron/coding-teacher/cloud-runner.ts | thread_id = topic.id;teacher_version = topic.version;local_context.scope 使用 topic.projectId | 可见聊天 ID 和云执行线程 ID 解耦 |
| electron/coding-teacher/store.ts | topic 文件包含全部 requests;list 读取各 topic 文件 | 改为分页读取,避免永久聊天全量加载 |
| electron/api/routes/coding-teacher.ts | 项目级 agent-topics 路由;SSE 发送完整 topic | 按联系人读写,事件只更新目标轮次 |
| src/pages/Chat/TeacherChatPanel.tsx、use-teacher-companion.ts | 话题选择和显式新建;状态、草稿以 account/project/agent 组织 | 归属改为 account/agent,项目只作上下文 |
这些结论来自本地源码和老师 ADR,不代表部署验收。本轮未检查 Yuxi 服务端线程绑定与压缩实现;实施前必须核实。
## 3. 数据结构
### AgentConversation:唯一可见聊天
- 唯一键:accountId + agentId。agentId 使用目录稳定 teacher_id,对应 definition.config_id,不使用名称、版本或默认标记。
- 保存 conversationId、创建/更新时间、最后消息、未读游标、阅读位置及当前执行段。
- 首次真正保存消息时由 Main 串行获取或创建;多窗口并发不能生成两个聊天。打开空面板不触发模型。
- 运营同步同一个配置沿用身份;删除后重新创建得到新配置 ID,视作另一智能体,不按同名合并。
- 当前展示资料使用已下发配置;历史保留执行版本及必要显示快照。
### ConversationTurn:每轮请求
在现有 TeacherRequest 上明确保存:
- requestId、conversationId、问题/引用/回答、状态、工具活动、用量。
- projectId、项目名称快照、sourceConversationId、来源游标和采集时间。
- teacherVersion、runtimeThreadId、云 request/run ID。
- 用户提问或主动建议来源、结构化讨论引用。
Main 接受时冻结项目、来源和版本,使用现有项目服务校验 Pi 会话归属并解析路径。Renderer 不传任意本地路径。流式回调按 requestId 更新目标轮,不能继续假设数组最后一项就是当前请求,因为主动建议可能同时到达。
### RuntimeSegment:内部执行段
建议同项目、同发布版本连续提问复用当前 Yuxi 线程。下一次发送发现项目或版本改变时,创建内部线程,带入有预算的近期交流及相关状态;旧消息仍显示在同一时间线。A → B → A 可以产生新执行段,不需要后台线程池。只切换页面或头像不建线程、不收费。
历史过长优先使用已验证的 Yuxi 压缩能力;若该能力不满足合同,可使用相同执行段机制轮转,并带入有界上下文。实施前验证服务端能力,不假定已有压缩,也不把全部历史不断追加到 query。摘要若调用模型,必须沿用学生计费链并明确触发,不新增定时总结调用。
这可利用现有 thread_id 参数;初步判断客户端为主要改动。Yuxi 是否需要补充压缩或恢复接口,在实施时依据其源码与测试确定。Works 继续负责目录、版本和学生身份,无需为此新增运营会话管理。
## 4. 发送与恢复
建议 Host API(名称在实施时按项目风格收敛):
- GET /api/coding/agents/:agentId/conversation:读取唯一聊天或空态。
- GET /api/coding/agent-conversations/:id/messages?before=...&limit=50:历史页。
- POST /api/coding/agents/:agentId/messages:幂等创建聊天、冻结上下文并接受问题。
- GET /api/coding/agent-conversations/:id/events:活动轮、未读和状态事件。
- POST /api/coding/agent-conversations/:id/requests/:requestId/cancel:取消精确请求。
断网、切页、重开只查询/订阅原状态,不重放计费问题。重复提交沿用 requestId。同一用户与智能体一次接受一轮主动问答;运行时可编辑草稿、等待或停止,先不引入消息队列。不同智能体沿用现有总运行约束。
历史页与事件按消息序号/revision 合并,避免分页覆盖流式结果;先显示最近 50 轮,向上加载,保持阅读锚点,阅读较早消息时不强制滚到底部。
每轮只读工具仍使用冻结 projectPath、Pi 消息快照、账号与取消状态;现有分页、预算和最终回答规则保持。其他项目的历史聊天文本不授予读取那些项目文件的权限。
草稿文字归用户+智能体,代码/消息引用携带项目与 Pi 来源。切项目后若草稿有旧引用,发送前明确保留旧上下文或移除引用,不能静默解释成新项目资料。“带回操作对话”定位消息原目标,不写入碰巧打开的另一会话。无有效项目时保留历史与草稿;普通无项目聊天需核对云合同,第一版可继续要求选择项目,不伪造上下文。
## 5. 配置与结构化讨论
版本改为“每轮接受时固定”:下一轮使用当前已下发版本;已运行轮次保持原版本;必要时切内部执行段,聊天不重建。停用禁止新调用,历史可读;同 ID 重启下发继续原聊天。运行中停用、取消和结算遵守原服务端规则。
现有 topic.discussion 不能直接合并成一个跨项目对象。卡片保留独立 ID、projectId、revision 与历史快照,当前只激活对应项目的讨论对象。操作旧项目卡片应明确恢复其来源上下文,不能套用当前项目卡片。
## 6. 持久化与旧历史
建议使用现有 app userData 根下新增账号隔离的 agent-conversations 目录,不改变全局路径配置。项目源文件和 Pi 会话继续在项目目录。移除项目不会连带删除用户与智能体的全部聊天。
使用轻量聊天索引加每轮独立文件,复用现有原子写文件能力,不新增数据库依赖。仅写当前轮和必要元数据;验证轮文件/索引在退出中断时可恢复,不能重发模型问题。
旧记录接入:
1. 只扫描应用已登记项目的既有老师历史目录、当前账号;不扫描整块磁盘。
2. 相同 definition.config_id 的旧话题归入同一聊天,按发生时间展示,保存 originProjectId/originTopicId/originRequestId;同时间用来源 ID 稳定排序。
3. 使用来源 ID 判定是否已接入,不按正文去重;重复问同一句话仍是两条消息。
4. 原文件保留,新结构写入成功才记录进度,重启可继续,不原地覆盖。
5. 无法证明身份的旧合成朋友/旧老师记录继续只读,通过单独旧记录入口查看,不按名字猜归属。
6. 失联项目稍后重新打开时再接入;不宣称已收集所有磁盘历史。
7. 不合并云端 checkpoint;旧运行结束或取消后启用新发送。只带入必要公开交流,不重新执行旧问题或上传完整工程。
8. 多份草稿只选择身份明确的一份恢复,其余保留供恢复,不用最后写入覆盖所有内容。
本方案先保持本机持久化。若需要同账号跨设备共享记录,必须增加服务端唯一会话与同步游标,文件读取仍绑定发起设备;各机器独立 UUID 无法保证跨设备唯一。
## 7. 并行主动观察任务
20260928-agent-observer-design-9c41a872 的记录显示正在实施项目选择观察者及轮数/空闲/冷却触发。其代码尚不属于本方案基线。建议由 Main 提供幂等的“向 accountId + agentId 聊天追加观察结果”操作,带 originatingProjectId 和 observationId。
观察调度仍按项目,无建议不制造空消息,有建议进入同一个聊天并产生对应头像未读。观察运行不能复用或挤占正在运行的主动问答线程。先前提到的按 requestId 更新可防止结果写错轮次。
实施前刷新该任务与 20260928-consultation-scope-83fa19c2 的记录;后者目前为模板、范围未知。明确 shared DTO、service、hook 和面板的文件所有权后再写产品代码。本轮仅写独立方案,不干扰并行实现。
## 8. 实施顺序与验收
1. 确认数据合同、云端线程/版本约束与并行接口,完成 Main 唯一存储和幂等发送。
2. 接入旧历史,冻结每轮项目/版本,验证执行段与云端恢复。
3. 替换话题 UI,接分页、上下文提示、草稿/阅读位置/未读。
4. 主动建议通过统一消息入口接入,完成联调。
实施必须验证:
- 双窗口、重试和重启只产生一个聊天和一次模型调用。
- 同智能体跨项目保留历史;A 请求运行中切 B,工具仍只读 A。
- 不同账号、智能体的消息/草稿/未读隔离。
- 下一轮采用新发布版本,运行中轮次不被切换。
- 重复正文、同时间旧消息、中断接入及失联项目不丢记录、不复制调用。
- 数千轮历史分页,流式结果不被翻页覆盖,阅读位置不跳动。
- 断网/关闭/取消/退出恢复不重发,不留假运行状态。
- 原项目或 Pi 来源删除后历史仍可读,旧引用/卡片不能误操作新项目。
- 主动建议和问答同时返回,分别更新正确记录,未读只追加一次。
- 顶栏打开即持续聊天,无学生新话题加号及话题下拉框。
设计阶段未运行产品测试、构建、真实收费模型或云端验收。实施时应做聚焦 Store/Service/API/Renderer 回归、Electron 切项目与分页流程、类型检查和生产构建。
## 9. 采纳与规范
待实施批准并验证后,在集成阶段更新老师 ADR、system-overview、data-flow、business-rules、README:
- 账号+项目+话题改为账号+智能体;
- 项目/Pi 来源改为每轮冻结上下文;
- 话题固定版本改为每轮接受时固定版本;
- 建议/讨论/问答共用联系人时间线,各自保留项目来源。
当前已接受决策保持不变。本提案不会把尚未实现的行为写成现状。
## 10. 实施结果(2026-09-28)
用户已批准实施和既有主动观察任务协调。单会话客户端已实现;具体数据、路由、验证和集成边界以[任务记录](../../30-worklog/tasks/20260928-agent-single-chat-27da516b.md)为准。
实际采用 `/api/coding/agent-conversations/:agentId` 系列接口;Main 原子保存轻量索引和独立轮文件,每页50轮,流事件仅传当前轮。云端源码协调结果显示线程同时绑定项目和Pi来源,故项目、Pi来源或版本改变都会切换内部线程;同段沿用现有Yuxi压缩,新段只携带有预算的公开历史。本机长历史可由只读工具按消息ID取回。
正文草稿、已读位置和旧记录已处理;滚动位置和已加载页的缓存只维持当前窗口生命周期。未进行跨设备同步或生产付费模型验收。旧原始记录全部保留,可从只读入口查看。
主动观察实现仍在另一任务独立审查中,尚未进入本分支。其“继续讨论”仍创建项目话题,必须在两项集成时改为同一聊天的幂等建议投递/定位;不能把接口协调记为组合功能已实现。单会话方向已经用户批准,共享规范的正式更新留给集成任务。