Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 111 additions & 0 deletions .specs/plans/2026-08-12_chat-welcome-expert-display/00-overview.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
# [2026-08-12] 聊天欢迎区展示专家自定义描述

> **本文件是本任务的单一真相源(Single Source of Truth)**:任务元信息、进度、当前步骤、关键决策全部在这里。
> 会话恢复时,先读本文件定位当前步骤,再按需加载对应阶段文件。
>
> ⚠️ 本项目**不维护**全局 `.specs/plan.md`——跨任务查看请列 `.specs/plans/` 目录。
> ⚠️ Meta 中的 `分支` 字段是上下文恢复时定位任务的唯一依据,**必须**与 `git branch --show-current` 的输出完全一致。

---

## Meta

| 项 | 值 |
|----|----|
| 分支 | `feature/chat-welcome-expert-display` |
| Issue / TAPD | `#188`(GitHub;无 TAPD) |
| 摘要 | 空聊天欢迎区 `@名称` 后优先展示 Agent 自定义 description;为空则回退 welcome_message |
| 状态 | ✅ 已完成 |
| 创建日期 | 2026-08-12 |
| 负责人 | 杨广知 |
| 预期完成 | 2026-08-12 |
| 开发模式 | 独立开发 |
| 测试环境 | |
| 预估代码改动行数 | ~25(不含测试 / 文档) |
| 小需求模式 | ⬜ 否 |
| 模型 | Cursor Grok 4.5 |

---

## Progress

<!-- 本文件结构 / 字段定义 / Progress / 时间记录 SOP 规则见
.specs/plans/_template/00-overview.md;本任务文件精简不重复。
规则变更只改 _template/,本任务文件由 init_specs.sh 后续刷新
不影响旧任务。 -->

- [x] 01. Clarify → [01-clarify.md](./01-clarify.md) (描述进聊天;空则 welcome_message 兜底)
- [x] 02. Plan → [02-plan.md](./02-plan.md) (前端 resolveWelcomeSuffix;~25 LOC)
- [x] 03. Implement → [03-implement.md](./03-implement.md) (纯函数 + hook 接线;Node smoke 7/7)
- [x] 04. UT → [04-ut.md](./04-ut.md) (Plan §6.1 7/7 PASS)
- [x] 05. Docs → [05-docs.md](./05-docs.md) (无对外文档变更)
- [x] 06. Review → [06-review.md](./06-review.md) (去掉多余 useMemo;批准合入)
- [x] 07. Commit → [07-commit.md](./07-commit.md) (fix(dashboard): prefer agent description…)

---

## 当前步骤

> 恢复会话时,优先读取此处指向的阶段文件。

- **步骤**:✅ 07. Commit(边界点 A 已锁定)
- **文件**:[07-commit.md](./07-commit.md)
- **上次更新**:2026-08-12 20:23:07

---

## 时间记录

<!-- 本文件结构 / 字段定义 / Progress / 时间记录 SOP 规则见
.specs/plans/_template/00-overview.md;本任务文件精简不重复。
规则变更只改 _template/,本任务文件由 init_specs.sh 后续刷新
不影响旧任务。 -->

| # | 步骤 | 开始时间 | 结束时间 | 耗时 | 对话轮次 | 备注 |
|---|------|---------|---------|------|---------|------|
| 01 | Clarify | 2026-08-12 15:10:26 | 2026-08-12 19:51:57 | 4h41m31s | 8 | Discovery + Challenge |
| 02 | Plan | 2026-08-12 19:53:36 | 2026-08-12 19:54:35 | 59s | 1 | |
| 03 | Implement | 2026-08-12 19:57:13 | 2026-08-12 20:03:47 | 6m34s | 1 | vitest 因本环境 npm 失败未跑;Node smoke 7/7 |
| 04 | UT | 2026-08-12 20:04:29 | 2026-08-12 20:07:55 | 3m26s | 1 | vitest 本环境未跑;Node strip-types 导入源码 7/7 |
| 05 | Docs | 2026-08-12 20:11:21 | 2026-08-12 20:11:38 | 17s | 1 | 清单全不涉及 |
| 06 | Review | 2026-08-12 20:20:52 | 2026-08-12 20:21:25 | 33s | 1 | 去掉多余 useMemo |
| 07 | Commit | 2026-08-12 20:22:25 | 2026-08-12 20:23:07 | 42s | 2 | TAPD 路径 C 跳过 |

---

## 关键决策备忘

> **跨阶段共享的关键上下文**。仅记录影响后续步骤的决策,避免恢复时还要翻阅历史阶段文件。

- 来源:[GitHub #188](https://github.com/TencentCloud/Octop/issues/188)
- **本轮 scope**:只做「描述进聊天欢迎区」;不新增标题/口号字段;不改 greeting 大标题
- **展示优先级**:非空 `agent.description` > `welcome_message`(manifest / 模板);空 description → `welcome_message`
- **布局**:`@名称` + 描述同一行;完整展示、允许自然换行、不截断
- **数据注意**:从模板新建会预填 description(`CreateFromExpertDrawer`),上线后多数 Agent 副文案会从短欢迎语变为表单描述
- **实现方案**:新增纯函数 `resolveWelcomeSuffix`;在 `useExpertChatWelcome` 组合;无后端 / DB 变更
- **TAPD**:路径 C 跳过——非 CVM 流程,仅跟踪 GitHub #188
- **Commit**: `fix(dashboard): prefer agent description on chat welcome`

---

## 风险速览

| # | 风险 | 严重度 | 缓解 |
|---|------|-------|------|
| 1 | 预填长 description 导致欢迎区文案整体变长 | 🟡 中 | 产品接受;靠现有 subtitle 样式自然换行 |
| 2 | #188 标题字段未做,Issue 可能不完全关闭 | 🟢 低 | 评论说明本轮只修描述一致性 |

---

## 文件索引

| 文件 | 产物 |
|------|------|
| [00-overview.md](./00-overview.md) | 任务总览(本文件) |
| [01-clarify.md](./01-clarify.md) | 需求澄清:背景、目标、范围、待确认问题 |
| [02-plan.md](./02-plan.md) | 方案设计:改动文件、调用链、数据模型、IT 用例 |
| [03-implement.md](./03-implement.md) | 实现:关键细节、与 Plan 差异、检查结果 |
| [04-ut.md](./04-ut.md) | 单元测试:用例、覆盖率、未覆盖行 |
| [05-docs.md](./05-docs.md) | 文档更新清单 |
| [06-review.md](./06-review.md) | Code Review:问题与修复 |
| [07-commit.md](./07-commit.md) | Commit message 与 amend 流程 |
81 changes: 81 additions & 0 deletions .specs/plans/2026-08-12_chat-welcome-expert-display/01-clarify.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
---
created: 2026-08-12
updated: 2026-08-12
---

# 聊天欢迎区展示专家自定义描述

## 需求卡片

**一句话目标**:空聊天欢迎区里,`@专家名` 后面展示该专家在设置里填写的「描述」;描述为空时回退到现有模板欢迎语。
**核心用户**:在 Dashboard「专家」页创建/编辑 Agent 的用户,进入「聊天」空会话时期望看到与设置一致的人设文案。
**做什么**:
- 欢迎区副文案优先使用 Agent 的 `description`(与表单「描述」字段一致)
- `description` 为空 / 未设置时,继续用现有 `welcome_message`(manifest / 模板欢迎语)兜底
- 展示形式保持现状:`@名称` + 描述,同一行,允许自然换行、完整展示
**不做什么**:
- 本轮不新增「标题 / 口号」字段(#188 截图中的标题字段延后)
- 不改大标题「嗨!你专属的智能伙伴来啦~」
- 不改快速开始卡片、侧边栏 Agent 卡片等其它展示位(除非实现时发现同一数据源必须顺带对齐)
**成功标准**:在「专家」里改描述并保存后,打开该 Agent 的空聊天欢迎区,`@名称` 后文案与表单「描述」一致;清空描述后恢复为模板欢迎语。

---

## 1. 背景 (Context)

[GitHub #188](https://github.com/TencentCloud/Octop/issues/188) 反馈:聊天欢迎区副文案与专家设置不一致。

现状(代码):
- `@名称` 已正确绑定 Agent `name`
- `@` 后文案来自 `GET /agents/{id}/chat/welcome` 的 `welcome_message`(manifest / 专家模板),**不是** Agent 表单里的 `description`

用户在「从模板新建 / 编辑专家」填写的「描述」会出现在专家列表等处,但空聊天欢迎区仍显示模板短欢迎语(例如系统医生的「描述系统症状,我来帮你做健康检查」),造成「名称对了、描述不对」的割裂感。

不做则:用户改完描述后在聊天里看不到,人设配置体验不闭环;#188 持续 open。

## 2. 目标 (Goal)

**主要目标**:
- 欢迎区副文案与专家设置「描述」字段保持一致
- 空描述时行为与线上一致(模板欢迎语兜底),避免空白副文案

**成功指标(可验证)**:

| 指标 | 当前值 | 目标值 | 验证方式 |
|------|-------|-------|---------|
| 有自定义 description 时欢迎区副文案 | 显示 welcome_message | 显示 description | 改描述 → 打开 `/chat/{agentId}` 空会话肉眼核对;UT 断言 WelcomeScreen / 数据优先级 |
| description 为空时副文案 | welcome_message | 仍为 welcome_message | 清空描述后刷新空聊天;UT |
| 布局 | `@名` + 后缀同一行 | 不变;长文自然换行、不截断 | UI 抽查长描述 Agent |

## 3. 风险点

| # | 风险 | 严重度 | 缓解 / 兜底 |
|---|------|-------|------------|
| 1 | 从模板新建会预填较长 description,上线后多数 Agent 副文案由短欢迎语变为长介绍,视觉变化面大 | 🟡 中 | Clarify 已确认「有描述就展示」;完整换行可接受;Plan 阶段注意 max-width / 现有 welcomeSubtitle 样式 |
| 2 | #188 原截图含「标题/口号」字段,本轮只做描述,可能被理解为未完全关闭 Issue | 🟢 低 | 明确本轮 scope;Issue 可评论说明剩余标题字段另开 / 后续迭代 |
| 3 | description 与 welcome_message 语义不同(介绍 vs 行动号召),混用可能弱化 CTA | 🟢 低 | 产品已选一致性优先;空描述仍保留欢迎语 CTA |

## 4. 待确认问题 (Open Questions)

| # | 问题 | 结论 | 决策人 |
|---|------|------|-------|
| 1 | 本轮是否同时做「标题/口号」字段? | 否,只做描述进聊天 | 杨广知 |
| 2 | 大标题 greeting 是否替换? | 否,保持「嗨!你专属的智能伙伴来啦~」 | 杨广知(Discovery 默认,未要求改) |
| 3 | description 为空时副文案? | 仍用现有模板 welcome_message 兜底 | 杨广知 |
| 4 | 有非空 description(含模板预填)是否一律覆盖 welcome_message? | 是,客户/表单描述优先展示 | 杨广知 |
| 5 | 描述与 `@名称` 是否分行? | 否,接在 `@名称` 后面同一行 | 杨广知 |
| 6 | 长描述是否截断? | 否,完整展示、允许自然换行 | 杨广知 |

## 5. 关联 (References)

- TAPD / Issue:[#188](https://github.com/TencentCloud/Octop/issues/188)
- 相关界面:`dashboard/src/pages/Chat/components/WelcomeScreen.tsx`;专家表单 `CreateFromExpertDrawer` / `EditAgentDrawer` 的 `description`
- 相关 API:`GET /api/agents/{agent_id}/chat/welcome`(`welcome_message` + `quick_prompts`);Agent 列表/详情中的 `description`
- 上游 / 下游:专家创建预填 `pickLocale(expert.description)`;聊天 `useExpertChatWelcome` + `activeAgent.name`

---

## 决策框架

1. **5W1H**:Why 设置与聊天不一致 / What 副文案用 description / Who 配专家的用户 / Where 空聊天 WelcomeScreen / When 有 description 时 / How 优先 description,空则 welcome_message
2. **INVEST**:范围小、可测、可独立交付;标题字段刻意砍掉以保持 Small
145 changes: 145 additions & 0 deletions .specs/plans/2026-08-12_chat-welcome-expert-display/02-plan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
# 02. Plan

> **目的**:把 Clarify 的结论转化为可落地的技术方案。
> **输入**:`01-clarify.md` 的目标与范围
> **输出**:改动清单、调用链、数据模型、**UT 用例(TDD 先行)**、IT 用例
> **TDD 模式**:本阶段必须**先于 Implement** 设计完 UT 用例(§ 6);UT/IT 边界与红绿循环约束详见 `04-ut.md` §0.5,本文件只列 UT 用例骨架,重复内容不复制。

---

## 1. 方案概述

前端最小改动:在聊天欢迎区副文案解析处增加优先级——**非空 `agent.description`(trim 后)优先于 API `welcome_message`**;二者皆空时保持现有行为(`WelcomeScreen` 再回退到 i18n `chatWelcome.descriptionWithAgentSuffix`)。

不改后端、不改 DB、不改 welcome API;`OctopAgent.description` 已由 `GET /api/agents` 提供。抽纯函数 `resolveWelcomeSuffix` 便于 TDD;在 `useExpertChatWelcome` 中组合 `agent.description` 与拉取到的 `welcome_message`,并让 effect / 返回值随 `description` 变化更新。

## 2. 改动文件清单

| 文件 | 改动类型 | 说明 |
|------|---------|------|
| `dashboard/src/pages/Chat/utils/resolveWelcomeSuffix.ts` | 新增 | 纯函数:`description` 非空 → 用之;否则 → `welcomeMessage` |
| `dashboard/src/pages/Chat/utils/resolveWelcomeSuffix.test.ts` | 新增 | §6.1 函数级 UT |
| `dashboard/src/pages/Chat/hooks/useExpertQuickCards.ts` | 修改 | 用 `resolveWelcomeSuffix(agent?.description, apiWelcome)` 作为返回的 `welcomeSuffix`;deps 含 `agent?.description` |

> 不改 `WelcomeScreen.tsx` 布局(已是 `@名` + 后缀同行 + 自然换行);不改 `Chat/index.tsx` 传参形状(仍传 `welcomeSuffix`)。

## 3. 影响范围

| 维度 | 影响 |
|------|------|
| 接口 | 无(复用已有 agents + chat/welcome) |
| 模块 | Dashboard Chat 欢迎区副文案数据源 |
| DB schema | 无 |
| 配置 | 无 |
| 协议兼容 | 无协议变更;仅前端展示优先级 |
| 上下游服务 | 无 |

## 4. 调用链

```
Chat/index.tsx
→ useExpertChatWelcome(activeAgent) // 修改:组合 description
→ agentChatApi.welcome(agentId) // 不变:拿 welcome_message + quick_prompts
→ resolveWelcomeSuffix( // 新增
activeAgent.description,
localized welcome_message
)
→ WelcomeScreen({ agentName, welcomeSuffix }) // 不变:@名 + welcomeSuffix 同行
```

原链路差异:原先 `welcomeSuffix` = 仅 `welcome_message`;现为 `description ?? welcome_message`(空串 / 纯空白视为空)。

## 5. 数据结构变更

### 5.1 内部 DataType / Schema

| 类型 | 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|------|
| (无新类型) | — | — | — | `resolveWelcomeSuffix(description: string \| null \| undefined, welcomeMessage: string \| null \| undefined): string \| null` |

### 5.2 DB 表结构

| 表 | 变更 | 索引影响 | 回滚方式 |
|----|------|---------|---------|
| — | 无 | — | — |

### 5.3 协议 / 接口契约

| 接口 | 新增字段 | 必填 | 兼容性 |
|------|---------|------|-------|
| — | 无 | — | 旧后端无需升级 |

## 6. UT 用例设计(TDD 必填,先于 Implement)

### 6.1 函数级 UT 用例

| # | 被测对象(函数 / 类 / 模块路径) | 测试文件(计划) | 类型 | 输入 | 期望输出 / 行为 | Mock 边界 |
|---|----------------------------------|------------------|------|------|----------------|-----------|
| 1 | `resolveWelcomeSuffix` | `dashboard/src/pages/Chat/utils/resolveWelcomeSuffix.test.ts` | 正向 | `description="系统描述"`, `welcomeMessage="模板欢迎语"` | `"系统描述"` | 无(纯函数) |
| 2 | 同上 | 同上 | 边界 | `description=""`, `welcomeMessage="模板欢迎语"` | `"模板欢迎语"` | 无 |
| 3 | 同上 | 同上 | 边界 | `description=" "`, `welcomeMessage="模板欢迎语"` | `"模板欢迎语"`(空白视为空) | 无 |
| 4 | 同上 | 同上 | 边界 | `description=null`, `welcomeMessage="模板欢迎语"` | `"模板欢迎语"` | 无 |
| 5 | 同上 | 同上 | 边界 | `description=null`, `welcomeMessage=null` | `null`(交由 WelcomeScreen i18n 兜底) | 无 |
| 6 | 同上 | 同上 | 正向 | `description=" 有空格 "`, `welcomeMessage="x"` | `"有空格"`(trim 后返回) | 无 |
| 7 | 同上 | 同上 | 逆向 | `description=undefined`, `welcomeMessage=undefined` | `null` | 无 |

> 幂等 / 外部异常:纯函数无副作用、无外部依赖,不适用。

### 6.2 场景 UT 用例(可选)

| # | 业务场景 | 入口 | 测试文件 | 类型 | 关键断言 | Mock 边界 |
|---|---------|------|----------|------|---------|-----------|
| — | 不强制 | — | — | — | 手工 / 后续可加 hook 测试 | — |

> CI 可跑 vitest 覆盖纯函数;WelcomeScreen 展示用手工 IT / 本地打开 `/chat` 验证即可。

## 7. IT 用例设计

| # | 场景 | 类型 | 前置条件 | 执行步骤 | 预期结果 |
|---|------|------|---------|---------|---------|
| 1 | 自定义描述展示 | 正向 | Agent 描述为「系统描述」 | 打开该 Agent 空聊天 | `@名称` 后为「系统描述」 |
| 2 | 空描述回退 | 边界 | 清空并保存描述 | 刷新空聊天 | `@名称` 后为模板 welcome_message |
| 3 | 长描述换行 | 正向(典型) | 描述为模板预填长文 | 打开空聊天 | 完整展示、自然换行、无省略号截断 |
| 4 | 无 Agent / 未选中 | 边界 | 无 activeAgent | 看欢迎区 | 走无 agentName 分支(既有 i18n description),不崩 |
| 5 | 改描述后刷新 | 正向 | 编辑专家改描述并保存,AgentContext refresh | 回到空聊天 | 副文案更新为新描述(若 refresh 后 description 已变) |

> 无写操作 API、无外部依赖故障路径;逆向「非法输入」不适用(前端展示字段)。不强制自动化 IT;本地 `octop run` + Dashboard 冒烟即可。

## 8. 风险与兜底

| 风险 | 触发条件 | 影响 | 缓解 | 回滚方案 |
|------|---------|------|------|---------|
| 上线后多数 Agent 副文案变长 | 模板预填 description | 欢迎区文案变「介绍向」 | Clarify 已接受;现有 `.welcomeSubtitle` max-width + 换行 | 回退 commit / 恢复仅用 welcome_message |
| 改描述后 UI 未更新 | AgentContext 未 refresh | 仍显示旧描述 | 依赖现有编辑保存后的 `refresh()`;effect deps 含 `description` | — |
| 误伤无 description 的旧 Agent | description 恒 null | 行为与线上一致 | 空则 welcome_message | — |

## 9. 工时估算

| 阶段 | 工时 | 备注 |
|------|------|------|
| Implement | 0.5–1h | 含 TDD 红绿 |
| UT | 含上 | 纯函数 UT |
| Deploy + IT | 0.5h | 本地冒烟 |
| Docs + Review | 0.5h | 可选简短 docs |
| **预估代码改动行数** | **~25** | **不含测试 / 文档;>10 → 小需求模式 ⬜** |

---

## 决策框架

1. **先画图**:见 §4 调用链。
2. **找相似**:复用 `Chat/utils/*` + colocated `*.test.ts` 模式(如 `threadTitle.ts`)。
3. **最小改动**:只改副文案优先级;不动 API / DB / WelcomeScreen 结构。
4. **边界优先**:空串、空白、null、双空均有 UT。

## 完成标志

- [x] 改动文件清单完整,每文件有说明
- [x] 调用链清晰
- [x] 数据结构变更含回滚方式(无 DB)
- [x] 函数级 UT 用例已设计(§6.1)
- [x] 场景 UT 已评估(不强制)
- [x] IT 用例覆盖正向 / 边界
- [x] 风险表有缓解与回滚
- [x] `00-overview.md` Progress / 当前步骤 / 时间记录已同步
- [x] 已与用户完成结束确认
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# 03. Implement

> **目的**:按 Plan 写代码,只记录改动点。
> **输入**:`02-plan.md`
> **输出**:代码改动 + 本文件

---

## 1. 改动文件清单

| # | 文件 | 改动摘要 |
|---|------|---------|
| 1 | `dashboard/src/pages/Chat/utils/resolveWelcomeSuffix.ts` | 新增纯函数:非空 description(trim)优先,否则 welcome_message |
| 2 | `dashboard/src/pages/Chat/utils/resolveWelcomeSuffix.test.ts` | Plan §6.1 七条 UT |
| 3 | `dashboard/src/pages/Chat/hooks/useExpertQuickCards.ts` | API welcome 与 `agent.description` 经 `resolveWelcomeSuffix` 组合后返回;Review 去掉多余 `useMemo` |

## 2. 与 Plan 的差异

| # | 偏离项 | 原因 |
|---|--------|------|
| | 无 | |

## 3. 自检

- [x] 无硬编码凭证 / Token / 密码
- [x] SQL 全部参数化(无 SQL)
- [x] 外部输入均有校验(trim 空白)
- [x] 错误路径有日志(welcome API catch 保持清空,既有行为)
- [ ] Lint / Format 通过(本环境 `npm install` 失败,未跑 dashboard lint;逻辑 smoke 已过)
- [x] Vitest 等价验证 7/7(`node --experimental-strip-types` 导入真实源码;正式 vitest 待本机 `npm install`)

---

## 完成标志

- [x] 所有改动文件已实现
- [x] 与 Plan 偏离项已记录
- [ ] 自检全部通过(vitest / lint 待本机 npm 可用后补)
- [x] `00-overview.md` Progress / 当前步骤 / 时间记录已同步
- [x] 已与用户完成结束确认

## 验证

- Node smoke(与 UT 用例同构):`OK 7/7`
- 本地建议:`cd dashboard && npm install && npm test -- src/pages/Chat/utils/resolveWelcomeSuffix.test.ts`
Loading