Compare commits

...
5 Commits
Author SHA1 Message Date
brother7 5e6866fc02 docs: 记录智能体图标修复集成结果
Electron E2E / Electron E2E (macos-latest) (push) Has been cancelled
Electron E2E / Electron E2E (ubuntu-latest) (push) Has been cancelled
Electron E2E / Electron E2E (windows-latest) (push) Has been cancelled
2026-09-26 16:29:42 +08:00
brother7 e3d01a1da4 merge: 合入下发智能体图标修复 2026-09-26 16:28:24 +08:00
brother7 a3c02e7464 fix: 显示下发智能体的云端图标 2026-09-26 16:11:09 +08:00
brother7 37e7b1d01c docs: 补齐小智硬件与固件平台介绍 2026-09-26 16:00:46 +08:00
brother7 c8e9a24a04 docs: 补充麦洛产品介绍与老师 Agent 定位 2026-09-26 15:53:59 +08:00
15 changed files with 538 additions and 12 deletions

No files matched your search

@@ -1,5 +1,8 @@
# System Overview
智能体图标跟随发布定义:Yuxi 将原生 `icon` 固定到发布快照,Works Square 转为公开地址 `icon_url` 下发。Makelore 顶栏、邀请、面板标题、欢迎区和消息行统一读取该字段;缺失或加载失败时回退到既有 `avatar_id`。新话题取当前发布定义,旧话题保持原版本;自定义图标使用正常图像渲染,内置像素头像保持像素样式。
详见[图标集成记录](../30-worklog/tasks/20260926-merge-agent-avatar-ml-92dc837e.md)。
Code 咨询由 Works Square 运营下发的 Yuxi 智能体执行,客户端不内置老师/朋友角色;发布定义提供名称、头像、人设、欢迎语和推荐问题。Main 按账号/项目管理本机话题,Renderer 按账号/项目/服务端智能体 ID 保存未发送草稿。每轮绑定当前操作会话和已完成 Pi 公开消息,执行云端请求的三个只读工具后主动回传结果;Yuxi 保存云端消息和收到的片段。学生支付咨询模型费用,个人 Agents 保持创建者付费。全部下发智能体在顶栏以头像和名称直接选择,溢出横向滚动;面板不再提供智能体下拉框。选择恢复对应历史或空输入框,不创建话题或调用模型。新话题使用所选或默认项,旧话题固定版本;旧未归属项目草稿、合成朋友历史及草稿仅查看。结构化讨论和现有主动跟进协议保留。正文只接收云端主线程文本,子线程事件仍推进续传游标。Code 默认 Agent、项目下会话和 Pi 执行保持原边界。详见[已接受决定](../10-decisions/ADR-2026-09-22-coding-teacher.md)。
## Current Architecture
@@ -4,6 +4,8 @@ This file is the integrated default-branch snapshot. Feature tasks record progre
## Integrated Through
- 2026-09-26:合入图标修复源 `a3c02e74649c743c445fb2304bec9e0ac954ad96`,合并提交 `e3d01a1da4c836d0f5c9d9fc42bd9257088a4669`。智能体图标跟随发布定义:Yuxi 将原生 `icon` 固定到发布快照,Works Square 转为公开地址 `icon_url` 下发。Makelore 顶栏、邀请、面板标题、欢迎区和消息行统一读取该字段;缺失或加载失败时回退到既有 `avatar_id`。新话题取当前发布定义,旧话题保持原版本;自定义图标使用正常图像渲染,内置像素头像保持像素样式。 产品与测试保持已审查源字节,复用源验证;7 份跨仓外来文档原样保留且未纳入提交。未推送、部署或更新安装版;发布配套三端后需在运营端同步 Yuxi 配置,新话题使用新版本。见[源任务](tasks/20260926-agent-avatar-ml-3c3686ec.md)及[本次集成](tasks/20260926-merge-agent-avatar-ml-92dc837e.md)。
- 2026-09-26:以已集成技术基线 `1037356090eca90793ffe74750c273cbb44d7aef` 为基础,记录并按用户进一步澄清修订[老师 Agent 产品定位](../00-brief/project-positioning.md#老师-agent-的产品定位):老师 Agent 是某一领域杰出人物的能力转化而成的智能体,供平台学生使用,提供专业理解、判断与解决问题的能力支持。蒸馏属于实现方法,产品定义围绕人的领域能力及学生价值。同步 README、老师 ADR 和决定索引;本次仅记录理念,不变更运行代码、线上配置或既有智能体角色模型,也不代表所有下发项已完成能力验收。
- 2026-09-24:从 `0e22e0545d6f58a17a8c85805570a550d1c14c43` 无冲突快进合入源 `79914e1d06a66f78239ebe57f816acc98dc1eaae`。智能体提交工具结果遇到同一 interrupted 父 Run 的临时 run_busy 时,Main 保留原批次等待资源释放后续接,不重读文件或新建问题,沿用取消与原问题时限;其他运行占用及冲突保持失败。产品/测试与源完全一致,沿用源 183 项相关测试及最终 29 项聚焦回归、Renderer 类型检查、scoped lint、Vite 构建和配套本地真实 HTTP/PG/worker 因果验证。原有 3 份文档保持,未推送、打包、安装或部署。详见[源任务](tasks/20260924-consultation-run-busy-9ac245f1.md)和[集成记录](tasks/20260924-merge-agent-rendering-ml-4efb196c.md);安装版/生产模型仍待验收。
@@ -12,12 +12,20 @@
## Scope
- 产品介绍补充(2026-09-26):用户指出遗漏小智硬件固件平台;在同一介绍中补入小智固件、设备服务、智控台、构建/OTA 及与麦洛的关系,修正总览与使用流程。其他工程仅作只读参考。
- 产品介绍扩展(2026-09-26):依据用户要求,将已确认的老师 Agent 定位扩展为三项目客观产品介绍,交付 `docs/product-introduction.md`。覆盖客户端四模块、平台作品/运营服务、Yuxi 能力平台、老师能力定义与当前交付边界;仅文档。
- 产品定位后续(2026-09-26):依据用户确认及进一步澄清,在 Makelore 产品文档记录“老师 Agent 是某一领域杰出人物的能力转化而成的智能体,供平台学生使用”。定义以人的领域能力为中心,蒸馏归入实现方法;仅更新文档。
- 工具续接后续集成(2026-09-24):合入已验证源 79914e1d06a66f78239ebe57f816acc98dc1eaae,修复同一 interrupted 父 Run 清理资源期间返回 run_busy 导致客户端取消问题。当前主线为源基线 0e22e0545d6f58a17a8c85805570a550d1c14c43。
- Merge reviewed source 97837cef90baf6bb02cfda0cef7b938081f3c5f7 (20260924-agent-rendering-a8c2d419) into local main; promote accepted consultation rendering and protocol facts. Paired Yuxi source: 02f27f33a8b816589036503f0720d446418c28aa.
## Intent And Constraints
- 小智补充的官方 check/start/status 与同一任务 Identity 匹配,Concurrent/Planning Gates Passed。重读入口、自身记录、Robot ADR-002/003,复用上一轮 canonical 上下文和未变化的 117 个 peer 范围。依据固件 README/audio/ota/MCP/builder、GoProjects 小智主服务路由及管理实现、WS xiaozhi_hardware_client/ai_hardware 核对职责;HardwareProjects 下旧服务副本只作参照,以已接入的外部 API 和当前主服务为准。只补产品介绍,不初始化或修改参考工程,不宣称老师已接入机器人或所有板型能力一致。
- 产品介绍后续:官方 check/start/status 与 Identity 和 integration 所有权匹配,Concurrent/Planning Gates Passed。重读三仓入口、自身记录、定位、状态、决定、架构、领域与相关产品说明;复用此前未变 peer 范围,刷新近期 20 份注册记录,当前三仓分别有 117/271/48 个 peer。头像同步和加权定价另有隔离任务,不作为已实现主线能力。历史模板记录仍未知只读,无本次具体依赖。计划:按真实入口核对功能,编写独立 Markdown,检查事实、结构、差异及文档写边界后交付;保留已 adopted 文档,不创建子智能体、不执行模型/线上调用。
- 用户进一步明确重点是老师 Agent 的定义“某领域杰出的人的能力变成一个 agent”。官方同任务续接核验与 Concurrent/Planning/Integration Gates 再次通过,重读本轮六份产品记录,复用未变的 116 个 peer 范围。计划:统一定义、能力内涵、学生价值和方法层次;核对文档差异、边界门禁后提交,不把蒸馏流程作为产品核心,也不收窄为单一聊天或判断技巧。
- 定位记录的 Concurrent/Planning/Integration Gates Passed:官方同任务续接与所有权核验完成,重读入口、自身记录、产品定位、当前状态、老师 ADR、README 与相关规范;此前 116 个 peer 范围复用,登记在上次集成之后无活动。用户 2026-09-26 的纠正及“记录下这个定位和理念”已授权本次产品定位确认。计划:在 project-positioning 集中记录能力来源、蒸馏内容、学生价值、建设路径及真实案例标准,以 README/ADR 链接引用,同步当前状态并完成文档检查。保留客户端不固化老师/朋友分类、云端配置及三个只读工具的既有边界;不修改代码、线上配置或宣称现有智能体已通过领域能力验收。
- 本轮继续既有智能体集成任务;官方 same-task start/status 与独占 integration lock、Identity 匹配。Concurrent/Planning/Integration Gates Passed;重读 entry、自身/源记录、当前集成状态及老师决定/架构/领域/证据等上下文,复用未变的 115 个 peer 范围,刷新本次已完成源。历史模板记录没有本次具体语义依赖。3 份既有 adopted 文档保持原样。计划:精确快进、核对源字节与提交祖先、接受 current-state 提议、通过文档门禁、提交完成并询问源工作树清理。产品字节一致时沿用源测试,不重复已完成检查;不推送、打包、安装、部署或创建子智能体。
@@ -28,6 +36,10 @@
## Outcome
- 已将产品组成补为四个主要部分,小智单列设备固件与设备服务两层;新增第六节(固件、语音、智控台、构建/升级、跨系统关系),增加机器人使用流程,同步 Robot 与 WS 对接说明。保留用户已确认的老师能力定义,区分 Yuxi 老师与小智机器人角色。
- 产品介绍交付:Makelore `docs/product-introduction.md` 包含整体分工、四个客户端模块、老师 Agent 定义与学生价值、三个只读工具、Works Square 网页及运营模块、Yuxi 资源/执行功能和典型流程。理念严格采用用户最新定义;蒸馏属于方法。核对后未将网页历史 AI 讲解文案、旧周额度、独立桌面 Learning 入口、待合入头像/计价和默认关闭的移动 Feed 写为现有常规能力。没有产品行为、部署或计费变更。
- 产品定位后续(2026-09-26):按用户进一步澄清,project-positioning、README、老师 ADR、决定索引和 current-state 已统一为“某一领域杰出人物的能力转化而成的智能体,供平台学生使用”。重点回到能力来源、所承载的领域能力及学生价值,蒸馏移到实现方法层;学生仍可获得领域理解、判断和解决问题的方法。既有交互和云端能力边界保持,未修改任何运行代码或线上定义。
- 工具续接后续:main 从 0e22e0545d6f58a17a8c85805570a550d1c14c43 无冲突快进至 79914e1d06a66f78239ebe57f816acc98dc1eaae。产品、测试、README 与源一致;源任务记录原样导入,仅在本集成记录和 current-state 收录结果。接受原 Run 清理期间复用原工具批次、保留截止时间与取消的提议,不改变云端互斥或计费。
- Source merge committed as 98559e32a75dfbada12a737d7ff0ffda55aa346a. Before the merge commit the drift checker could not recognize the staged foreign source record; exact source-record equality was confirmed, then the unchanged source import was committed and the official drift check passed. No foreign record was edited/adopted to bypass this gate.
@@ -36,6 +48,12 @@
## Verification
- 小智补充核对通过:当前固件构建说明与 ota/audio/MCP 实现、GoProjects 小智管理端路由和 WS 外部设备接口支持新增职责描述;八个章节、表格列数、原老师定义及总览范围检查通过,git diff --check 和官方 task-aware drift 通过。本轮没有硬件烧录、语音测试、构建或部署,不据此声明真机兼容;3 份 adopted 文档保持。
- 产品介绍文档检查已通过:三仓官方 task-aware drift 与 git diff --check 均通过,既有 adopted 文档保持原状态。Markdown 的 UTF-8、表格列数、七个主章节、末尾换行和老师 Agent 定义核对通过;本轮仅新增介绍文件及三份自身记录,无运行代码变化。未执行产品测试、站点构建或生产验收;Yuxi 只变更私有任务记录,不改公开文档站。
- 本轮依据:Makelore README、`src/lib/ai-modules.ts`、AIHardware/CloudAgents 页面、项目提交入口和老师 ADR;Works Square Operations 菜单、CodingTeacherOperations/ObservationAgentOperations、Education 当前视图及平台已接受决定;Yuxi ARCHITECTURE、路由、AgentManage/Extensions 页面与原生老师决定。验证限定于本地主线文档/源码事实,不代表线上功能或领域能力验收。正式文档门禁、空白与本轮精确提交范围检查在交付前执行;不重复运行与纯介绍文档无关的模型或产品回归。
- 定义澄清后再次检查六份文档差异,git diff --check 和官方文档门禁通过。原链接路径与标题锚点保持,3 份 adopted 外来文档未变。纯文字修订未运行应用测试或构建。
- 定位记录:检查六个文档的差异与新增链接/标题目标,git diff --check 和官方 task-aware documentation drift 均通过;3 份 adopted 外来文档保持原样、未纳入提交。此次只有产品文档变更,未运行与内容记录无关的应用测试或构建,也未把理念确认表述为已下发智能体的能力验收结果。
- 工具续接后续:source ancestry、全部产品文件/source-record 精确相等、无 unmerged 路径检查通过。复用已验证源 183 项相关测试、最终 29 项聚焦回归、Renderer typecheck、scoped ESLint 与 Vite 全目标构建;配套 Yuxi 控制验证证明清理前 409、清理后相同批次 200 并完成回答。验证结果未被表述为新的重复测试或生产验收。官方文档门禁及 git diff --check 通过,3 份已 adopted 外来文档仍为原始状态;未改变产品或测试 oracle。
@@ -0,0 +1,49 @@
# Task: Diagnose distributed agent avatar propagation
## Identity
- Task ID: 20260926-agent-avatar-ml-3c3686ec
- Mode: Feature
- Branch: codex/20260926-agent-avatar-ml-3c3686ec-agent-avatar-ml
- Worktree: D:\Datas\OthersProjects\.codex-worktrees\makelore\20260926-agent-avatar-ml-3c3686ec
- Base commit: 9147b509c71f3ea765e5aaa8f542fd0a74bd4653
- Owner: codex
- Status: Ready for Integration
## Scope
- Diagnose and repair the missing Yuxi icon across publication, distribution and client rendering. User authorized implementation after the source diagnosis.
## Intent And Constraints
- Preserve immutable publication/replay and old-topic semantics, student billing, source storage ownership and existing local avatar fallback.
- No live configuration, deployment, automatic synchronization, main-branch merge or modification of peer worktrees.
- Concurrent ownership and Planning Gate passed in the same isolated task worktree. Same-base project memory and peer task scopes were retained from diagnosis; no new peers at resumption. Historical placeholder peer scopes remain unknown with no identified semantic conflict.
- Plan completed: froze the icon in Yuxi, resolved and preserved it in Works Square, rendered it consistently in Makelore, then verified each boundary. The one user-authorized fresh read-only reviewer approved the complete change with no blocking findings.
## Outcome
- Added optional icon_url to the distributed TeacherDefinition. TeacherAvatar handles the configured image and falls back to avatar_id when absent or when loading fails; new image URLs can be attempted after an earlier failure.
- Topbar entries, legacy/history companion entry, invitation speaker, panel header, welcome and assistant message rows use TeacherAvatar. Custom images use normal rendering; bundled pixel art keeps pixelated rendering.
- Updated README and added actual UI/browser regressions.
## Verification
- RED: both new tests failed with built-in avatar src instead of the expected cloud URL before implementation.
- GREEN: pnpm exec vitest run tests/unit/teacher-companion.test.tsx tests/unit/coding-teacher-ui.test.tsx: 56 passed.
- pnpm exec tsc --noEmit and focused ESLint passed. pnpm run build:vite passed for renderer, Main, preload and utility worker; existing Browserslist/chunk warnings remain.
- Real Edge Playwright distributed-icons case passed, including actual image decoding, independent 404 fallback, normal image rendering and the invitation speaker. Screenshot inspected in test-results; test route supplies deterministic image bytes, not production OSS.
- No installed Electron package or live production visual acceptance.
## Follow-ups
- Independent review passed. One non-blocking evidence gap remains: no combined test updates catalog metadata while an old topic is already open; existing separate data-flow tests and source inspection support the pinned-topic behavior.
- Task documentation drift checks passed across all three repositories; commit the scoped result for integration.
- Deploy compatible Yuxi, Works Square and Makelore versions, then use Operations sync to publish the existing agent's icon. Older topics keep their original published definitions.
## Promotion Candidates
- Target: .project-docs/20-architecture/system-overview.md and integrated current-state when useful.
- Proposal: record the versioned icon contract: native Yuxi icon becomes a frozen teacher snapshot field, WS resolves the public URL, client renders it with local fallback.
- Evidence: production diff plus regression results above. Future impact: visual identity follows the same publication boundary as other agent metadata.
- Semantic conflicts: none identified. Human confirmation: implementation authorized; canonical promotion and main-branch merge remain a separate integration task.
@@ -0,0 +1,41 @@
# Task: Integrate distributed agent icon repair
## Identity
- Task ID: 20260926-merge-agent-avatar-ml-92dc837e
- Mode: Integration
- Branch: main
- Worktree: D:\Datas\OthersProjects\makelore
- Base commit: 37e7b1d01c438cd5cd0da7b8ada432b06970d150
- Owner: codex
- Status: Ready for Integration
## Scope
- Merge reviewed source a3c02e74649c743c445fb2304bec9e0ac954ad96 from task 20260926-agent-avatar-ml-3c3686ec into the local primary branch; promote the published agent icon contract.
## Intent And Constraints
- Concurrent, Planning and Integration Gates Passed. Official start/status match Identity and exclusive integration ownership. Read entry, own/source records, positioning/current-state/decisions/architecture/domain and relevant evidence/commitment indexes; refreshed registered peer scope sections and reused unchanged prior context. Historical placeholder scopes have no concrete dependency on this merge; isolated pricing work is out of scope.
- User explicitly approved release --force of the completed prior primary owner and --adopt-existing for the known foreign documents. Preserve their exact states and exclude them from commits. Source task records remain read-only.
- Plan: merge the exact source; verify ancestry, unchanged product/test/source-record bytes and retained main documentation; promote accepted icon facts; pass documentation/whitespace gates, commit and complete. Reuse the independent review and source tests when product bytes match. No new subagents, push, deployment, model calls or worktree deletion.
## Outcome
- Merged exact source a3c02e74649c743c445fb2304bec9e0ac954ad96 without conflicts as e3d01a1da4c836d0f5c9d9fc42bd9257088a4669. All changed product/test and source-task files match the reviewed source; Makelore README retains the newer main-branch positioning paragraph.
- Promoted the approved versioned icon contract into system-overview and current-state. Existing foreign documents remain unchanged and outside the index; no source record was rewritten.
## Verification
- Source/staged file equality, absence of unmerged paths and git diff --cached --check passed before the merge commit. Independent source review passed with no blocking findings; no implementation changes required another reviewer.
- Source evidence reused for identical product/test bytes: 56 focused unit tests, renderer typecheck, focused ESLint, Vite renderer/Main/preload/worker build and real Edge image decoding/404 fallback case passed. Installed-client and production OSS acceptance remain unverified.
- Final source ancestry, exact product/test tree and imported records, clean unchanged source worktree, scoped document diff, whitespace and official task-aware drift passed. Main-only README/product-introduction text is preserved. The official drift check confirmed all adopted foreign document states unchanged. No new fingerprints or validation infrastructure were added.
## Follow-ups
- Compatible three-end deployment, Operations configuration sync and real-client image acceptance remain separate. Older topics keep their published definition; a new topic uses the synchronized icon.
- Source worktree D:\Datas\OthersProjects\.codex-worktrees\makelore\20260926-agent-avatar-ml-3c3686ec on branch codex/20260926-agent-avatar-ml-3c3686ec-agent-avatar-ml is retained awaiting post-merge cleanup confirmation. Removing it would also remove ignored dependencies/build output; branches and commits must remain.
## Promotion Candidates
- Accepted source proposal applied to system-overview and current-state under integration ownership. No architecture direction, billing or historical-version conflict remains.
+1
View File
@@ -180,6 +180,7 @@ Pi 正式包必须继续运行 `pnpm run verify:artifact:pi`、`pnpm run smoke:p
- 当前输入是按预算截取的所选操作对话已完成分支文本、明确引用、咨询历史和适用的当前组件,并支持上述只读原文恢复;没有跨全部操作会话自动读取,也未向操作 Agent 注入咨询共识。“先这些”仍仅结束本轮整理,保留各条目状态,不确认全部候选、不自动制作。提示词不宣称这些系统能力已接通,也不声称看过未提供的画面或试玩过作品。Yuxi 话题经云端原生执行,旧版本地智能体由 Main 调用计费网关;均不创建本地 Pi 编程 worker。
- 智能体输入裁剪区分两种计量:本地模型以约每 2 个 UTF-8 字节估算 1 Token,并计入工具与读取预留;这不是精确分词或扣费依据,模型上限与实际 usage 仍由服务端拥有。云端沿用 query 字节上限,以完整 JSON 转义后的大小裁剪来源节选。智能体指令、当前问题和明确引用不会被静默裁掉;固定配置或当前整理本身超限时明确提示调整配置,避免让学生反复缩短短问题或新建话题。完整来源仍可由只读工具按需读取。
- `tests/fixtures/teacher-guidance-examples.json` 提供宠物、种植、射击、蛋糕的人工多轮输入输出样例,由真实解析器、上下文编译器与讨论状态机校验;样例不加载到运行时,也不代表真实模型教学效果已验收。云端定义由 Yuxi 维护并由运营显式同步下发;本地代码修改不会自动改写云端配置或学生已有话题的固定发布版本。
- 下发智能体的图标随发布版本保存,顶栏、咨询面板、欢迎区和消息使用定义中的 `icon_url`;未设置或图片加载失败时使用内置头像。运营在 Yuxi 更换图标后需同步下发配置,新话题采用新版本,已有话题保持原图标。相对图片地址由服务端使用 Yuxi 公网入口补全。
- 咨询话题及未发送草稿按当前云端账号、项目保存;切换操作对话仍延续该项目下的咨询,每轮发送时绑定当时的操作对话来源。创建话题时固定发布版本,每轮重新检查启停。关闭面板或切换会话不取消已接受的问题,退出账号会中止。请求先落盘再调用模型,同一请求标识不重复发送,重启后未完成请求标记中断。云端断线按游标和持久状态恢复;停止或退出账号会取消原云端问题,下次提问先停止旧的未完成问题再绑定新上下文。只接收云端主线程回答,子智能体文本不混入正文。没有操作对话时也可咨询,但无操作文本上下文。
- 删除操作对话会停止正在使用该来源的回复,保留项目级咨询历史;归档来源需先恢复后才能引用。原有来源会话级智能体 API 与存储保持兼容,原路径历史不自动迁移到新项目级咨询栏。运营草稿试聊仍使用 `niancode://coding-teacher/preview?draft_revision=N` 打开独立示例上下文,不读取学生项目,要求桌面当前账号具有运营权限。
+347
View File
@@ -0,0 +1,347 @@
# 麦洛产品介绍
> 整理日期:2026 年 9 月 26 日。本文介绍 Makelore、Works Square、Yuxi 与小智硬件及固件平台组成的产品体系,依据当前项目源码、功能说明与已确认的产品定位整理。具体可用功能取决于账号权限、客户端和固件版本、硬件配置及配套服务。
## 一、产品概述
麦洛是一套面向学生和创作者的 AI 创作与学习产品。用户可以通过对话编写程序、制作图片和视频、配置机器人、创建个人智能体,也可以在做项目的过程中向平台提供的领域智能体请教。
产品围绕具体的项目、作品和问题展开:用户提出想法,借助 AI 完成制作和修改,在需要专业判断时引入老师 Agent,并通过作品平台展示符合发布条件的成果。
整个产品体系由四个主要部分组成,其中小智部分包含设备固件与设备服务两个层次:
| 项目 | 产品职责 | 主要使用者 |
| --- | --- | --- |
| **Makelore 桌面客户端** | 提供 AI 编程、视觉创作、机器人管理、个人云智能体和项目内咨询的操作界面 | 学生、创作者、个人智能体使用者 |
| **Works Square 平台** | 提供网页作品展示、账号与点数服务、作品审核发布、资源管理及智能体下发 | 学生、作品浏览者、家长、平台运营人员 |
| **Yuxi 智能体平台** | 管理智能体及其模型、提示词、知识库和扩展能力,承担云端智能体的对话与执行 | 智能体配置人员、能力建设者、平台管理员 |
| **小智硬件与固件平台** | 提供设备端固件、语音交互服务、机器人配置、设备管理及固件构建与升级能力 | 机器人使用者、硬件开发者、设备运营与维护人员 |
对学生而言,Makelore 是主要的创作入口。Works Square 承担平台服务与运营管理,Yuxi 提供可配置、可运行的云端智能体能力,小智体系承担实体机器人上的交互和设备运行。
## 二、Makelore 桌面客户端
客户端提供四个模块入口。各模块共用账号,是否开放由平台配置。
### 2.1 Makelore Code:AI 编程
AI 编程模块以本地项目为单位,支持用户通过自然语言与编程智能体协作,将想法制作成可运行的应用、游戏或工具。
主要功能包括:
- **项目管理**:选择本地目录创建或打开项目;新项目自动建立默认编程智能体,无需单独完成智能体创建流程。
- **项目会话**:会话直接列在项目下,支持新建、重命名、归档、恢复和查看历史。
- **编程协作**:围绕项目文件开展代码生成、修改、运行与问题排查,查看执行过程和代码变化。
- **作品预览**:在工作区查看项目运行结果,结合实际页面继续调整作品。
- **模型与上下文**:选择可用模型及其思考选项,通过文本、图片、文件上下文等提供需求。
- **项目资源配置**:为项目配置模型、知识与插件;通过插件和 Skill 扩展编程能力。
- **智能体咨询**:在当前项目旁打开平台下发的智能体,讨论设计、方法和具体问题。
- **提交审核**:符合条件的交互式项目可从项目配置发起“一键提交审核”,由客户端完成相应准备与提交,再进入平台审核发布流程。
编程操作对话与智能体咨询分别保存。前者承担项目制作和修改;后者结合项目资料提供分析、判断和讨论。用户可以在同一个工作区使用两种能力。
### 2.2 Makelore Canvas:AI 绘画与视频制作
Canvas 用于通过对话整理视觉创作需求,并生成图片或视频。每个设计项目保存持续更新的制作方案、对话和历史作品。
主要功能包括:
- **设计项目管理**:创建、切换和管理不同创作项目。
- **对话整理需求**:与 AI 讨论内容、风格和制作方向,形成可继续修改的方案。
- **方案编辑**:修改创作提示词及当前支持的媒介、画幅、数量、时长等参数。
- **参考图管理**:上传本地参考图,或使用历史作品作为后续创作素材;视频制作支持选择开始画面。
- **确认后生成**:查看方案及费用信息,确认后启动制作任务。
- **任务与结果管理**:查看生成状态与历史结果,对图片放大预览,对视频播放、预览和下载。
创作方案、任务和生成结果归属于对应的云端设计项目,便于在同一方向上连续修改和制作。
### 2.3 Makelore Robot:AI 机器
Robot 用于管理接入平台的机器人设备及其对话配置。
主要功能包括:
- **机器人智能体管理**:创建和选择机器人智能体,编辑名称与角色提示。
- **设备激活与绑定**:通过设备激活码绑定设备,并将设备分配给对应智能体。
- **引导配网**:在支持的桌面环境中引导连接设备热点,继续完成设备配网。
- **模型配置**:从设备服务提供的可用目录中选择相关模型。
- **语言与声音配置**:设置对话语言、声音音色及聊天记录方式。
- **设备与配置查看**:围绕所选智能体查看绑定设备和当前配置。
Robot 是小智硬件体系在 Makelore 中的管理入口。设备上的固件、实时语音对话及固件升级由小智体系承担,完整职责见第六节。设备行为与可选能力受对应硬件和设备服务支持范围约束。
### 2.4 Makelore Agents:个人云智能体
Agents 模块用于创建、配置和使用个人云智能体。用户可以定义智能体的任务、知识和工具,在试用后发布一个正式使用版本。
主要功能包括:
- **创建与配置**:编辑智能体需求、名称、提示词、模型、能力和使用限制。
- **草稿试聊**:在配置过程中测试智能体的表现,并继续调整。
- **发布版本**:将配置发布为正式使用版本,查看版本差异,或将历史版本恢复为草稿。
- **正式对话**:与已发布智能体聊天,查看历史、运行状态、附件和生成文件。
- **知识库**:创建知识库,上传、替换和管理资料,查看处理状态,并将可用知识配置给智能体。
- **扩展能力**:根据可用资源配置 Skill、工具、MCP 服务和子智能体等能力。
- **自动任务**:配置定时或重复任务,管理启停、运行记录、结果与相关费用限制。
- **微信渠道**:连接个人微信账号,为账号选择已发布的智能体,并管理连接状态和渠道对话。
- **应用接入**:为应用配置 API 接入,使已发布智能体能在其他应用中被调用。
- **费用与用量**:查看相关用量和费用,设置智能体的费用限制。
个人云智能体由创建者配置和管理,其应计费模型调用由创建者承担。它与平台选定并下发给学生的老师 Agent 分别管理。
### 2.5 客户端公共功能
四个模块之外,客户端提供账号登录、个人资料、模块权限、永久词元点数余额、充值订单、收支记录和应用更新等公共功能。模型选项和能力按照平台下发的实际可用配置展示。
## 三、老师 Agent:将领域杰出人物的能力转化为智能体
### 3.1 定义与定位
**老师 Agent,是某一领域杰出人物的能力转化而成的智能体,供平台学生用户使用。**
这一产品定义围绕三个问题展开:它承载谁的能力、这些能力属于什么领域、学生能够从中得到什么帮助。
“能力”包括对领域问题的理解、专业经验、观察方式、判断标准、取舍方法和解决问题的能力。老师 Agent 的价值体现在学生遇到具体问题时,能够获得这些能力所带来的专业判断与行动参考。
例如,游戏设计领域的老师可以帮助判断玩法是否成立、反馈是否清晰、当前最值得改进的环节是什么;创作领域的老师可以帮助判断表达是否准确、作品特点是否形成、不同修改方向会带来什么影响。这些是能力方向的示例,实际下发内容以平台配置和能力验证为准。
### 3.2 为学生提供什么
学生可以带着正在进行的项目、作品或困惑,与老师 Agent 讨论。预期获得的帮助包括:
- **识别问题**:从领域视角找到关键问题和容易遗漏的因素。
- **形成判断**:分析当前方案的合理性、局限和需要补充的证据。
- **解释依据**:说明为什么这样判断,以及判断依赖的条件。
- **比较选择**:梳理不同方向的收益、代价和取舍。
- **提出下一步**:给出与当前项目阶段相匹配的尝试和改进建议。
- **理解方法**:让学生在具体讨论中逐步理解优秀实践者如何观察、思考和处理问题。
不同老师 Agent 可以承载不同领域、不同人物的能力与方法。学生根据问题选择合适的智能体,并保留自己的选择和决定权。
### 3.3 能力如何进入产品
提示词、Skill、案例、知识库与工具是承载能力的具体方式。能力总结、提炼和蒸馏属于实现方法;老师 Agent 的产品定义以人的领域能力及其对学生的价值为中心。
能力建设需要明确来源、擅长领域、具体能力和适用边界,并通过真实问题检验其判断是否有依据、是否有帮助、是否能随新事实调整。名称、头像和表达风格用于呈现智能体的身份。
这一理念是产品建设与能力评价的依据。现有的智能体配置和下发功能,并不代表所有已配置智能体都已经完成领域能力验证。
### 3.4 当前提供的使用方式
当前老师 Agent 主要通过 Code 项目的智能体咨询栏提供服务:
- 平台可下发多个智能体,客户端在顶栏并排展示入口,学生点击相应智能体开始或继续讨论。
- 智能体名称、人设、职责和能力由云端配置决定;客户端统一呈现,不设置固定的“老师”“朋友”等角色分类。
- 每个智能体保留相应话题与历史;讨论可围绕当前编程会话、明确引用和项目资料展开。
- 回答支持 Markdown、列表、表格、代码、图片和公式;适用时可用想法板、结构图、流程或对照等形式辅助整理。
- 工具活动与回答分别展示,学生可以看到智能体使用了哪些工具及其状态。
- 新话题使用当时下发的发布版本;更新配置后,已有话题仍保留其原版本。停止下发后,相关历史仍可查看。
### 3.5 如何理解项目与会话
客户端为云端老师智能体提供三项与当前上下文绑定的只读能力:
| 能力 | 可以提供的信息 | 使用目的 |
| --- | --- | --- |
| 列出项目文件 | 当前项目的文件目录与文件信息 | 了解项目结构,判断需要查看哪些资料 |
| 读取项目文件 | 当前项目范围内允许读取的文本文件内容 | 结合实际代码、配置或说明进行分析 |
| 读取会话原文 | 当前绑定操作会话及相关咨询上下文中可读取的公开内容 | 理解此前需求、尝试过程和讨论依据 |
资料由客户端按请求提供给云端。完整工程和编程运行日志保留在本地,云端保存对话及实际收到的资料片段。读取范围受当前项目、会话和访问规则约束,不等于自动遍历用户所有项目与全部历史。
这三项能力对本地项目是只读的。老师 Agent 的建议由学生决定如何采用;当前系统也不会自动把所有咨询结论写入编程操作会话或自动执行修改。
### 3.6 配置、下发与计费
老师 Agent 在 Yuxi 中配置能力,由 Works Square 运营端选择、发布和下发,Makelore 负责学生侧使用与本地上下文读取。
运营人员可以查看已下发列表、添加智能体、同步 Yuxi 配置、设置默认项、启用或停止下发。老师问答沿用学生账号付费规则,与个人云智能体的创建者付费规则分别处理。
## 四、Works Square:作品平台与运营服务
### 4.1 网页作品平台
Works Square 提供学生作品的展示与访问入口,支持浏览已发布作品、查看详情、打开交互式作品和观看视频。网页还提供账号中心、本人项目及相关提交状态的管理入口。
交互式作品经过提交、校验和审核后,按明确的发布版本提供访问。平台管理发布、下架和版本回退,便于控制对外展示的作品内容。
### 4.2 账号、家庭与费用服务
平台统一处理账号与个人资料、组织成员、模块开放权限、永久词元点数钱包、充值订单、消费记录及计费规则。
家庭关系支持邀请与共享付款来源管理。账号的内容归属与付款来源分别管理,共享付款不会直接开放他人的项目或会话内容。
平台还接收经过筛选的编程会话自然语言快照,提供观察分析与报告相关服务。观察智能体在运营端单独配置,与学生主动咨询的老师 Agent 分别承担功能。
### 4.3 内容与资源服务
- **学习项目目录**:管理经过整理的学习项目,包含标题、简介、说明文档、封面和项目文件包,支持发布、排序与历史版本管理。
- **设计服务**:为 Canvas 提供云端方案、制作报价、生成任务和结果资产管理。
- **模型服务**:管理平台可用模型、使用资格与能力信息,供客户端及相关智能体选择。
- **插件服务**:管理官方插件目录、发布资源和账号获取记录,配合客户端完成项目使用。
- **客户端分发**:管理安装包和更新信息。
- **老师 Agent 下发**:将运营选定的 Yuxi 智能体提供给有资格的学生账号使用。
- **机器人服务对接**:连接麦洛账号与小智设备服务,提供机器人智能体创建、配置、设备绑定和设备分配所需的接口。
### 4.4 运营后台模块
| 模块 | 主要功能 |
| --- | --- |
| 发布审核 | 查看待审、历史和失败记录,预览作品,批准或驳回发布 |
| 视频审核与视频作品 | 管理视频提交、预览、审核和发布相关状态 |
| 平台发布项目 | 查看和管理对外发布的项目及其版本 |
| 学习项目管理 | 编辑项目资料与文件,发布、排序、归档和查看历史 |
| 组织用户 | 管理组织成员及账号关联、权限和模块开放状态 |
| 充值档位与词元点数计费规则 | 维护充值配置、计费单位和消费规则 |
| Design Agent 配置 | 管理视觉创作智能体的配置及发布版本 |
| 观察 Agent | 配置会话观察和报告相关智能体能力 |
| 编程老师 | 查看下发列表,从 Yuxi 添加智能体,同步版本、设为默认及启停 |
| 插件管理 | 管理插件内容、资源、开放状态和相关配置 |
| 模型管理 | 管理平台模型的可用状态与相关业务配置 |
| 安装包管理 | 管理客户端安装包与更新分发信息 |
| 手动上传 | 由运营人员提交项目资料与文件 |
## 五、Yuxi:智能体能力配置与云端执行
Yuxi 为产品体系提供智能体的配置、知识资源与运行能力,同时提供独立的网页智能体使用界面。
### 5.1 智能体管理与问答
- 创建和编辑原生主智能体,配置名称、描述、系统提示词及模型。
- 配置工具、Skill、MCP、知识库和子智能体等资源。
- 在问答页面使用智能体,查看流式回答、工具活动、附件和历史会话。
- 管理云端执行过程,处理排队、停止、恢复和运行结果。
- 为个人云智能体提供草稿、发布版本、定时任务及应用接入所需的执行服务。
### 5.2 知识与扩展资源
- **知识库**:上传和管理资料,进行解析、索引与检索,并查看处理和可用状态。
- **知识评估**:通过评估相关功能检查知识检索效果。
- **Skill**:以可配置的任务方法和说明扩展智能体能力。
- **工具与 MCP**:连接智能体可调用的操作和外部服务。
- **子智能体**:将具体职责交给独立配置的子智能体协作处理。
- **工作区与文件**:管理云端会话相关的文件和产物,提供查看与使用入口。
不同资源的管理和使用受账号权限约束。
### 5.3 模型与平台管理
管理员可以配置模型供应商、聊天模型及知识处理所需的模型。Yuxi 普通聊天模型与下发给学生的老师模型分别配置,老师模型需要匹配学生模型网关的可用目录。
在老师 Agent 的协作链路中,Yuxi 负责能力定义和云端运行,Works Square 负责选择与下发。运营端可选择平台原生主智能体目录中的智能体,目录覆盖符合条件的内置及其他用户创建项,不局限于当前运营账号名下。
## 六、小智硬件与固件平台
小智体系让 AI 通过实体设备与用户交互。它包括运行在设备上的 ESP32 固件,以及处理语音对话、管理设备和配置的服务端。固件工程提供板型适配和构建能力,服务平台提供智控台及 OTA 升级管理。
### 6.1 设备端固件
设备固件负责连接具体硬件,把麦克风、扬声器、屏幕和可用执行部件组织成可交互的终端。
主要能力包括:
- **硬件适配**:按芯片和板型配置音频、显示、按键及其他外设,构建对应的设备固件。
- **网络与配网**:连接设备网络,通过支持的配网方式设置连接信息,并与小智服务通信。
- **语音输入与播放**:处理麦克风采集、语音唤醒、音频传输和扬声器播放。
- **交互反馈**:在配有屏幕、灯光等部件的设备上显示状态、文字或表情。
- **设备控制**:通过设备实际提供的工具控制音量、显示、灯光、GPIO 或电机等能力。
- **激活与升级**:获取激活信息,检查适用固件版本并执行 OTA 更新。
带摄像头的设备可提供视觉输入,具备相应音频条件的设备可支持更完整的回声消除和语音交互。具体能力取决于板型、外设、固件构建选项和服务配置,不能把所有板型视为具有相同功能。
### 6.2 语音与智能交互服务
小智服务端接收设备的音频和消息,组织语音识别、模型回答、工具调用与语音合成,再把结果返回设备。
主要能力包括:
- **语音对话**:提供语音活动检测、语音识别、语言模型对话和语音合成,支持流式交互。
- **角色配置**:为机器人配置角色提示、语言、模型和声音。
- **模型接入**:对接不同的语音、语言和视觉模型服务,按配置使用对应能力。
- **记忆与知识**:提供可选的记忆、知识检索等能力,辅助连续对话和领域问答。
- **声纹与视觉**:在相应服务与设备启用后,支持说话人识别及视觉理解相关功能。
- **工具和设备指令**:通过工具、插件或 MCP 等方式扩展行为,并向支持的设备下发控制指令。
这些属于小智体系的可配置能力,是否对某台机器人开放取决于其配置与实际接入的服务。
### 6.3 智控台与设备运营
小智提供网页智控台,并包含移动管理端,用于管理机器人服务本身。主要模块包括:
| 模块 | 主要功能 |
| --- | --- |
| 智能体与角色配置 | 管理机器人角色、模型、声音及相关对话配置 |
| 设备管理 | 管理设备绑定、归属及与机器人智能体的关联 |
| 用户与接入管理 | 管理用户,以及外部平台使用设备服务的接入凭据 |
| 模型与供应商管理 | 配置语音识别、语言模型、语音合成等服务 |
| 声音与声纹 | 管理音色资源、声纹及已接入的声音扩展能力 |
| 知识库 | 管理机器人使用的知识资源及检索配置 |
| 固件与 OTA | 管理固件类型、版本、文件及升级分发 |
| 系统配置 | 管理服务参数、功能配置和相关运行设置 |
Makelore Robot 面向日常设备使用,小智智控台承担更完整的设备服务配置与维护;两者的界面和功能范围有所不同。
### 6.4 固件构建与交付
固件工程支持按板型及发布变体构建,可选择界面语言和支持的唤醒词模型。构建产物包含用于设备升级的固件、用于完整烧录的镜像,以及构建日志和版本来源信息。
设备维护人员可以将适用固件交付给设备烧录,或通过固件与 OTA 管理流程提供升级。板型与固件必须匹配;构建成功并不等于对应实物设备已经完成音频、屏幕、网络和升级验证。
### 6.5 与麦洛其他部分的关系
| 部分 | 在机器人链路中的职责 |
| --- | --- |
| Makelore Robot | 向用户提供创建角色、引导配网、激活绑定、设置声音和分配设备的操作入口 |
| Works Square | 处理麦洛账号侧的授权、关联记录与小智设备服务对接 |
| 小智服务平台 | 保存和使用机器人配置,处理设备通信、语音对话与设备管理 |
| 小智设备固件 | 在实物设备上采集输入、播放声音、显示反馈及执行支持的设备操作 |
当前机器人智能体沿用小智体系,平台下发的老师 Agent 由 Yuxi 运行。现有产品介绍不将“在 Yuxi 配置一位老师”表述为“该老师已自动进入所有机器人”;把同一领域能力用于实体设备,需要另外确认能力接入、语音交互和设备支持情况。
## 七、典型使用流程
### 7.1 学生制作一个交互式作品
1. 在 Code 中创建或打开项目,描述想做的作品。
2. 与编程智能体协作完成代码和运行预览。
3. 遇到设计或方法问题时,从顶栏选择合适的老师 Agent。
4. 老师结合当前问题、项目文件和会话资料给出判断与建议。
5. 学生选择采用的建议,继续制作和验证。
6. 对符合发布条件的作品提交审核,通过平台流程对外展示。
### 7.2 用户制作图片或视频
1. 在 Canvas 创建设计项目,描述内容并提供参考素材。
2. 通过对话和参数编辑确认制作方案。
3. 查看费用并确认生成。
4. 在历史作品中预览、下载结果,或继续用于后续制作。
### 7.3 平台提供一位领域老师
1. 明确某位领域杰出人物的能力来源、擅长范围和要提供给学生的帮助。
2. 在 Yuxi 中通过提示词、Skill、案例、知识及工具配置承载这些能力,并检查实际表现。
3. 运营人员在 Works Square 选择该智能体,发布并下发。
4. 学生在 Makelore 中看到入口,并在自己的项目情境中使用。
5. 能力配置调整后,由运营显式同步新版本,供后续新话题使用。
### 7.4 用户配置一台机器人
1. 为支持的设备准备匹配的固件,并完成必要的烧录或升级。
2. 在 Makelore Robot 创建机器人智能体,设置角色、模型和声音。
3. 按引导为设备配网,使用设备提供的激活码完成绑定。
4. 将设备分配给对应智能体,通过设备实际测试语音与交互表现。
5. 日常在客户端调整公开配置,由设备维护人员通过小智平台管理适用的固件和服务配置。
## 八、当前产品范围说明
- Makelore 当前提供四个模块入口。学习项目目录属于平台服务和运营能力,当前不作为客户端的第五个独立模块。
- 客户端支持项目内提交审核;独立作品广场主要由网页平台承担。
- 学习项目以运营整理的内容和项目文件为主。自动课程生成、完整教学进度系统和课堂管理不属于本文所述现有功能。
- 老师 Agent 的专业能力定位适用于后续能力建设;当前主要使用入口位于 Code 项目咨询,并不表示所有模块都已接入同样的项目读取能力。
- 个人云智能体、编程操作智能体、平台下发的咨询智能体和机器人配置各有当前职责与使用入口,本文不将它们视为已经完全互通的配置对象。
- 小智固件及服务能力按硬件和配置启用;设备绑定成功不等于在线、语音链路或全部外设已通过验证。Yuxi 老师 Agent 与机器人角色目前也不自动同步。
- 手机端混合信息流等仍有独立启用和验收条件的能力,未列为本介绍中的常规可用功能。本文也不将尚未合入主分支的改动列为现状。
---
本文依据 Makelore、Works Square、Yuxi、小智服务端和小智固件工程的模块入口、源码、已接受产品决策及功能说明整理。老师 Agent 定义采用 2026 年 9 月 26 日确认的定位。本文用于产品介绍,不作为特定部署环境或实物设备的功能验收报告。
+1
View File
@@ -16,6 +16,7 @@ export interface TeacherDefinition {
name: string;
description: string;
avatar_id: string;
icon_url?: string | null;
welcome_message: string;
suggested_questions: string[];
system_prompt: string;
+17
View File
@@ -0,0 +1,17 @@
import { useState } from 'react';
import { getAgentAvatarSrc } from '@/lib/agent-avatars';
import type { TeacherDefinition } from '../../../shared/coding-teacher';
/** 展示发布版本的图标,缺图或加载失败时使用原有内置头像。 */
export function TeacherAvatar({ definition, className }: {
definition?: Pick<TeacherDefinition, 'avatar_id' | 'icon_url'> | null;
className?: string;
}) {
const [failedIcon, setFailedIcon] = useState<string | null>(null);
const icon = definition?.icon_url;
const custom = Boolean(icon && icon !== failedIcon);
return <img className={className} alt=""
src={custom ? icon! : getAgentAvatarSrc(definition?.avatar_id)}
style={{ objectFit: 'contain', imageRendering: custom ? 'auto' : 'pixelated' }}
onError={custom ? () => setFailedIcon(icon!) : undefined} />;
}
+4 -5
View File
@@ -12,7 +12,7 @@ import type {
} from '../../../shared/coding-teacher';
import { TeacherDiscussionPanel, DiscussionFocus } from './TeacherDiscussionPanel';
import type { TeacherDiscussionAction } from '../../../shared/coding-teacher';
import { getAgentAvatarSrc } from '@/lib/agent-avatars';
import { TeacherAvatar } from './TeacherAvatar';
import { TeacherReply } from './TeacherReply';
export function TeacherChatPanel({
@@ -67,7 +67,6 @@ export function TeacherChatPanel({
const [historyOpen, setHistoryOpen] = useState(false);
const [focus, setFocus] = useState<{ topicId: string; id: string; label: string } | null>(null);
const [definition, setDefinition] = useState<TeacherDefinition | null>(null);
const avatar = getAgentAvatarSrc(definition?.avatar_id);
const externalTopicRef = useRef(externalTopic);
externalTopicRef.current = externalTopic;
const selectedAgentId = selectedAgent?.teacher_id;
@@ -383,7 +382,7 @@ export function TeacherChatPanel({
<aside className="consultation-pane flex h-full min-h-0 w-full flex-col"
data-testid="teacher-chat-panel" aria-label={`${label}聊天`}>
<header className="consultation-header flex h-[68px] shrink-0 items-center gap-3 px-5">
<img className="consultation-avatar h-9 w-9 rounded-xl [image-rendering:pixelated]" src={avatar} alt="" />
<TeacherAvatar className="consultation-avatar h-9 w-9 rounded-xl" definition={definition} />
<div className="min-w-0 flex-1"><h2 className="truncate text-sm font-semibold">{definition?.name ?? '智能体'}</h2>
<p className="mt-1 text-[11px] text-muted-foreground">{draftRevision ? '运营草稿试聊' : definition?.description}</p></div>
<button aria-label={`${label}新话题`} title="新话题" disabled={busy || !enabled} onClick={() => void newTopic()} className="consult-icon"><Plus className="h-4 w-4" /></button>
@@ -407,7 +406,7 @@ export function TeacherChatPanel({
}} />}
<div ref={scrollRef} className="consultation-messages min-h-0 flex-1 overflow-y-auto px-5 py-6">
{!topic?.requests.length && <div className="flex min-h-full flex-col items-center justify-center pb-5 text-center">
<img className="mb-6 h-[68px] w-[68px] rounded-3xl [image-rendering:pixelated]" src={avatar} alt="" />
<TeacherAvatar className="mb-6 h-[68px] w-[68px] rounded-3xl" definition={definition} />
<h3 className="text-lg font-medium tracking-tight">{definition?.welcome_message || '开始对话'}</h3>
<div className="mt-6 flex w-full max-w-64 flex-col gap-2">{questions.map((question) => <button key={question} onClick={() => changeText(question)} className="consultation-suggestion flex items-center justify-between gap-2 rounded-lg border bg-white px-3 py-3 text-left text-xs">{question}<ChevronRight className="h-3 w-3 shrink-0 opacity-50" /></button>)}</div>
</div>}
@@ -416,7 +415,7 @@ export function TeacherChatPanel({
? <p className="text-[11px] text-muted-foreground">智能体来看看你的进展</p>
: <div className="flex justify-end"><p className="consultation-user max-w-[92%] whitespace-pre-wrap break-words rounded-2xl rounded-br-sm px-4 py-3 text-[13px] leading-7">{request.text}</p></div>}
{request.references.length > 0 && <details className="text-xs text-muted-foreground"><summary>本轮引用 {request.references.length} 项</summary>{request.references.map((ref, index) => <pre className="whitespace-pre-wrap" key={index}>{ref.text}</pre>)}</details>}
<div className="flex items-center gap-2 text-[11px] text-muted-foreground"><img src={avatar} alt="" className="h-6 w-6 rounded-lg [image-rendering:pixelated]" />{definition?.name ?? label}</div>
<div className="flex items-center gap-2 text-[11px] text-muted-foreground"><TeacherAvatar definition={definition} className="h-6 w-6 rounded-lg" />{definition?.name ?? label}</div>
{!!request.toolActivity?.length && <details className="text-xs text-muted-foreground">
<summary className="cursor-pointer">使用了 {request.toolActivity.length} 项工具</summary>
<ul className="mt-2 space-y-2" aria-label="智能体工具活动">
+4 -5
View File
@@ -1,6 +1,6 @@
import { ArrowUpRight, Loader2, X } from 'lucide-react';
import type { Ref } from 'react';
import { getAgentAvatarSrc } from '@/lib/agent-avatars';
import { TeacherAvatar } from './TeacherAvatar';
import type { useTeacherCompanion } from './use-teacher-companion';
type Companion = ReturnType<typeof useTeacherCompanion>;
@@ -12,7 +12,6 @@ export function TeacherCompanion({ companion, open, onOpen, bubbleRef }: {
}) {
const { definition, invitation, welcome, loading, enabled, checking, error } = companion;
const name = definition?.name || '智能体';
const avatar = getAgentAvatarSrc(definition?.avatar_id);
const activeId = companion.selectedAgent?.teacher_id;
const agents = companion.selectedAgent && !companion.agents.some((item) => item.teacher_id === activeId)
? [...companion.agents, companion.selectedAgent] : companion.agents;
@@ -28,13 +27,13 @@ export function TeacherCompanion({ companion, open, onOpen, bubbleRef }: {
aria-expanded={open && activeId === agent.teacher_id} aria-pressed={open && activeId === agent.teacher_id}
aria-controls="coding-consultation-dock" data-agent-id={agent.teacher_id}
onMouseDown={(event) => event.preventDefault()} onClick={() => onOpen(agent.teacher_id)}>
<span className="teacher-companion-avatar"><img src={getAgentAvatarSrc(agent.definition.avatar_id)} alt="" />
<span className="teacher-companion-avatar"><TeacherAvatar definition={agent.definition} />
{activeId === agent.teacher_id && invitation && <span className="teacher-companion-unread" aria-hidden="true" />}</span>
<span className="teacher-companion-name">{agent.definition.name}</span>
{activeId === agent.teacher_id && checking && <Loader2 className="h-3 w-3 shrink-0 animate-spin text-muted-foreground motion-reduce:animate-none" aria-label="智能体正在思考" />}
</button>)}
{(!agents.length || (!activeId && companion.topic)) && <button type="button" className="teacher-companion-person" title={status} aria-label="智能体" aria-expanded={open} aria-controls="coding-consultation-dock" onMouseDown={(event) => event.preventDefault()} onClick={() => onOpen()}>
<span className="teacher-companion-avatar"><img src={avatar} alt="" />{invitation && <span className="teacher-companion-unread" aria-hidden="true" />}</span>
<span className="teacher-companion-avatar"><TeacherAvatar definition={definition} />{invitation && <span className="teacher-companion-unread" aria-hidden="true" />}</span>
<span className="teacher-companion-name">{name}</span>
{checking && <Loader2 className="h-3 w-3 shrink-0 animate-spin text-muted-foreground motion-reduce:animate-none" aria-label="智能体正在思考" />}
</button>}
@@ -52,7 +51,7 @@ export function TeacherCompanion({ companion, open, onOpen, bubbleRef }: {
</div>
</div>
<button type="button" className="teacher-companion-speaker" aria-label="打开这条智能体消息" onMouseDown={(event) => event.preventDefault()} onClick={viewConversation}>
<img src={avatar} alt="" />
<TeacherAvatar definition={definition} />
</button>
</div>}
</section>
@@ -17,11 +17,12 @@ export function Layout() {
const [selected, setSelected] = useState(0);
const agents = Array.from({ length: count }, (_, index) => ({
teacher_id: `agent-${index}`, version: index + 1, is_default: index === 0,
definition: { config_id: `agent-${index}`, name: `智能体${index + 1}`, avatar_id: 'avatar-06' },
definition: { config_id: `agent-${index}`, name: `智能体${index + 1}`, avatar_id: 'avatar-06',
icon_url: new URLSearchParams(location.search).has('icons') ? `https://yuxi.example.test/api/storage/public/images/agent-${index}.png` : undefined },
}));
const companion = {
agents, selectedAgent: agents[selected],
definition: { name: '编程老师', avatar_id: 'avatar-06' },
definition: agents[selected]?.definition ?? { name: '编程老师', avatar_id: 'avatar-06' },
invitation: visible ? { response: long ? '你刚才说想做一个宠物游戏。我们一起想想,什么样的小动作,会让你愿意一直陪它玩下去?可以先挑一个最想试的想法。' : '有什么编程问题想一起讨论?' } : null,
welcome: null, enabled: true, loading: false, checking: false, error: null,
dismiss: () => setVisible(false),
+20
View File
@@ -70,6 +70,26 @@ async function geometry(page: Page) {
});
}
test('distributed icons load over HTTP and broken images fall back in the titlebar', async ({ page }) => {
await page.route('https://yuxi.example.test/api/storage/public/images/agent-0.png', route => route.fulfill({
contentType: 'image/svg+xml',
body: '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32"><circle cx="16" cy="16" r="15" fill="blue"/></svg>',
}));
await page.route('https://yuxi.example.test/api/storage/public/images/agent-1.png', route => route.fulfill({ status: 404 }));
await page.goto(`${baseUrl}?agents=2&icons=1`);
const custom = page.getByRole('button', { name: '与智能体1聊天' }).locator('img');
const fallback = page.getByRole('button', { name: '与智能体2聊天' }).locator('img');
const speaker = page.getByRole('button', { name: '打开这条智能体消息' }).locator('img');
await expect(custom).toHaveAttribute('src', 'https://yuxi.example.test/api/storage/public/images/agent-0.png');
await expect(custom).toHaveCSS('image-rendering', 'auto');
await expect(speaker).toHaveAttribute('src', 'https://yuxi.example.test/api/storage/public/images/agent-0.png');
await expect(fallback).toHaveAttribute('src', /avatar-06\.png/);
for (const img of [custom, fallback, speaker]) {
await expect.poll(() => img.evaluate(node => (node as HTMLImageElement).naturalWidth)).toBeGreaterThan(0);
}
await page.screenshot({ path: test.info().outputPath('distributed-icons.png') });
});
for (const size of [{ width: 1440, height: 900 }, { width: 1180, height: 720 }, { width: 1000, height: 640 }]) {
test(`preview only clears overlapping speech at ${size.width}x${size.height}`, async ({ page }) => {
await page.setViewportSize(size);
+10
View File
@@ -96,6 +96,16 @@ async function ready() {
await waitFor(() => expect(streams.has('first')).toBe(true));
}
describe('teacher side chat', () => {
it('renders the pinned topic icon in the header and assistant messages', async () => {
const icon = 'https://yuxi.example.test/api/storage/public/images/topic.png';
api.read.mockResolvedValue({ ...first, definition: { ...definition, icon_url: icon }, requests: [request({ response: '继续原来的讨论' })] });
const view = render(<TeacherChatPanel projectId="p" sourceId="c" />);
await ready();
const images = view.container.querySelectorAll('[data-testid="teacher-chat-panel"] img');
expect(images).toHaveLength(2);
for (const img of images) expect(img).toHaveAttribute('src', icon);
});
it('renders assistant Markdown, tables, code and images instead of literal markup', async () => {
const response = '## 项目建议\n\n**先验证跳跃**\n\n- 保留现有玩法\n\n| 文件 | 用途 |\n| --- | --- |\n| main.ts | 入口 |\n\n```json\n{"name":"flybot"}\n```\n\n[说明](https://example.com/readme)\n\n![项目截图](https://example.com/preview.png)';
api.read.mockResolvedValue({ ...first, requests: [request({ response })] });
+18
View File
@@ -43,6 +43,24 @@ beforeEach(() => {
afterEach(() => { cleanup(); vi.useRealTimers(); });
describe('teacher presence and periodic guidance', () => {
it('uses each published icon in the topbar, welcome and panel, and falls back after an image error', async () => {
const a = { teacher_id: 'a', version: 1, is_default: true, definition: { ...definition, icon_url: 'https://yuxi.example.test/api/storage/public/images/a.png' } };
const b = { teacher_id: 'b', version: 2, is_default: false, definition: { ...definition, name: '另一个智能体', icon_url: 'https://yuxi.example.test/api/storage/public/images/b.webp' } };
api.catalog.mockResolvedValue({ items: [a, b] });
const view = await mount();
const first = screen.getByRole('button', { name: '与运营配置的智能体聊天' }).querySelector('img')!;
const second = screen.getByRole('button', { name: '与另一个智能体聊天' }).querySelector('img')!;
expect(first).toHaveAttribute('src', a.definition.icon_url);
expect(second).toHaveAttribute('src', b.definition.icon_url);
fireEvent.error(first);
expect(first.src).toContain('avatar-06.png');
expect(second).toHaveAttribute('src', b.definition.icon_url);
await act(async () => fireEvent.click(screen.getByRole('button', { name: '与另一个智能体聊天' })));
const images = view.container.querySelectorAll('[data-testid="teacher-chat-panel"] img');
expect(images).toHaveLength(2);
for (const img of images) expect(img).toHaveAttribute('src', b.definition.icon_url);
});
it('shows every distributed agent in the header and preserves separate drafts without dispatching on switch', async () => {
const a = { teacher_id: 'method', version: 3, is_default: true, definition: { ...definition, config_id: 'method', name: '方法老师' } };
const b = { teacher_id: 'experience', version: 9, is_default: false, definition: { ...definition, config_id: 'experience', name: '体验朋友', avatar_id: 'avatar-09' } };