# 会话级系统提示词覆盖 ## 背景 当前系统提示词仅支持 Agent 级别配置(`agents.systemPrompt` 字段),由管理员在后台 `/admin/agent-config` 页面设置。所有用户使用同一 Agent 时共享相同的系统提示词,无法按会话自定义。 用户希望能在每个会话中自行传入系统提示词,无需管理员后台配置。 ## 目标 - 用户可在会话级别覆盖 Agent 默认系统提示词 - 不传时回退到 Agent 默认值 - 管理员配置的 Agent 默认提示词保留作为兜底 ## 范围 - 数据库:`agentSessions` 表新增 `systemPrompt` 字段 - 后端:创建会话接口支持传入、配置接口支持更新、chat-engine 优先级调整 - 前端:会话设置入口(编辑系统提示词)、展示当前生效的提示词 - 匿名用户:同样支持(通过 tempToken 关联会话) ## 非目标 - 用户级偏好(跨会话持久化)—— 后续迭代 - 提示词模板库 —— 后续迭代 - 提示词版本历史 —— 后续迭代 ## 架构 ### 优先级规则 ``` 会话级 systemPrompt(trim 后非空)> Agent 默认 systemPrompt ``` 会话级为 `null` 或空字符串/纯空白时,回退到 Agent 默认。前端保存空内容时传 `null`,不区分"清空覆盖"和"未设置"——两者行为相同,都使用 Agent 默认。 ### 数据层 `agentSessions` 表新增字段: | 字段 | 类型 | 默认值 | 说明 | |------|------|--------|------| | `systemPrompt` | `text` | `null` | 会话级系统提示词,null 表示用 Agent 默认 | 迁移命令:`bun run db:generate && bun run db:migrate` ### 后端改动 #### 1. 创建会话接口 `POST /api/agents/:agentSlug/sessions` - `index.post.ts`:从 body 读取可选 `systemPrompt` 参数 - `createSession` service:新增 `systemPrompt` 参数,写入数据库 - 字符长度限制:5000 字符(与管理员配置一致) #### 2. 会话配置接口 `PUT /api/agents/:agentSlug/sessions/:id/config` - `config.put.ts`:支持 `systemPrompt` 字段更新 - `updateSession` service:`data` 参数类型新增 `systemPrompt?: string | null` - 传 `null` 表示清除覆盖,回退 Agent 默认 - 鉴权:现有 `getSessionByIdAndUser(id, userId, tempToken)` 已处理匿名用户通过 tempToken 鉴权,无需额外改动 #### 3. Chat Engine `executeChat` - `chat-engine.ts:251`:修改优先级逻辑 ```ts const systemPrompt = session.systemPrompt?.trim() || agent.systemPrompt || undefined; ``` #### 4. 会话详情接口 `GET /api/agents/:agentSlug/sessions/:id` - `index.get.ts` 直接返回整个 `session` 对象(`R.success({ session, messages })`),新增 `systemPrompt` 字段后自动返回,**无需改动该接口** ### 前端改动 #### 1. `AgentSessionItem` 类型 `app/composables/useAgentSessions.ts`:新增 `systemPrompt: string | null` 字段 #### 2. `updateSessionConfig` 方法 扩展 config 参数,支持 `systemPrompt?: string | null` #### 3. 系统提示词弹窗改造 `AgentSystemPromptModal.vue`:从只读展示改为可编辑 - 展示当前生效的提示词(会话级或 Agent 默认) - 提供编辑 textarea - 保存按钮调用 `updateSessionConfig` - 显示"使用 Agent 默认"提示,区分覆盖状态 #### 4. `AgentToolbar.vue` 系统提示词按钮图标从 `lucide:info`(只读)改为 `lucide:settings`(可编辑),title 改为"系统提示词设置" #### 5. 页面层 `chat/[agentSlug]/index.vue` - `systemPromptText` 改为 computed:`sessions.currentSession.value?.systemPrompt ?? agentSystemPrompt`(会话级优先,回退 Agent 默认) - `loadSystemPrompt` 保留,用于加载 Agent 默认提示词作为兜底展示 - 弹窗保存后更新会话数据,`systemPromptText` 自动响应 ## 数据流 ``` 用户点击工具栏"系统提示词"按钮 → 弹窗展示当前生效提示词(session.systemPrompt ?? agent.systemPrompt) → 用户编辑并保存 → PUT /api/agents/:slug/sessions/:id/config { systemPrompt: "..." } → updateSession 写入 agentSessions.systemPrompt → 弹窗关闭,前端更新本地 session 数据 用户发送消息 → POST /api/agents/:slug/sessions/:id/chat → executeChat 读取 session.systemPrompt → 优先级:session.systemPrompt?.trim() || agent.systemPrompt → streamText({ system: resolvedPrompt, ... }) ``` ## 错误处理 - 系统提示词超过 5000 字符:返回 400 "系统提示词不能超过5000字符"(注:`agents.systemPrompt` 字段本身无长度限制,5000 为业务限制) - 未登录用户修改非自己会话:返回 403(现有 `getSessionByIdAndUser` 已通过 tempToken 鉴权处理) - 保存失败:toast 提示"保存失败" ## 测试 - E2E:创建会话时传入自定义提示词 → 发送消息 → 验证回复受自定义提示词影响 - E2E:会话中修改提示词 → 后续消息使用新提示词 - E2E:清空提示词 → 回退到 Agent 默认 - 单元:`executeChat` 优先级逻辑