Guidance Assistant 不是独立大模型实现。Application 组装页面上下文,经 IAiSessionAdapter 交给 Infrastructure;Adapter 创建或复用 AIManage Conversation,并保存一条 Guidance Session 映射。
1. 启用条件
Adapter 每次 Start/Send/Stream 都读取 Guidance:EnableAiAssistant,只有可解析为 true 才继续。Start 还要求 Guidance:AiWorkspaceId;为兼容旧配置会回退 Guidance:WorkspaceId。
{ "Guidance": { "EnableAiAssistant": true, "AiWorkspaceId": "workspace-guidance-prod" }}缺开关返回 Guidance.Assistant.Disabled,缺 Workspace 返回 Guidance.Assistant.WorkspaceRequired。这是失败关闭,不是回退到内容响应;非 AI 客户端应独立调用 Contextual API。
2. Start 时序
Content 可以为空,Assistant 仍会用页面/身份上下文创建会话。产品需决定“无权威指南时允许通用问答”还是失败关闭;当前 Prompt 只要求模型承认找不到指南。
3. SessionId
GuidanceSessionId.Create 把 Trim 后的 route、component 或 (page)、language 用 | 拼接,SHA-256 后 Base64Url,再返回 guidance-{userId}-{hash}。TenantId 不在 hash 中,但实体查询同时绑定 TenantId 与 UserId。
var grid = GuidanceSessionId.Create( userId, "/billing/invoices", "grid", LanguageCode.ZhCn);var form = GuidanceSessionId.Create( userId, "/billing/invoices", "form", LanguageCode.ZhCn);
// SessionId 不包含原始路由斜杠,两个组件也不会复用同一 Prompt。Debug.Assert(grid != form);Debug.Assert(!grid.Contains('/'));SessionId 是确定性的,不是调用幂等令牌。它会把数字 UserId 暴露在前缀中,也没有 TenantId/PromptVersion/GuideVersion;不要把不可猜测性当作授权。
4. Session 持久化
GuidanceAssistantSession 是 TenantAggregateRoot<string>,唯一索引 (TenantId, UserId, SessionId),保存 ConversationId、RouteKey、ComponentId 和 CreateTime,并支持软删除字段。
它不保存 LanguageCode(只隐含在 hash)、GuideId/Version、PromptHash、WorkspaceId、ModelId、LastUsedAt、ExpiresAt 或关闭原因。模块也没有会话列表、关闭、删除和清理用例。
5. Prompt 内容
Builder 固定使用中文系统指令,随后明文加入 RouteKey、ComponentId、LanguageCode、TenantId、UserId、Roles;若有指南,再加入 Title、ContentVersion 和最多 16,000 字符 BodyMarkdown。
你是 BitzOrcas 法律 SaaS 的应用内页面向导助手。=== 当前上下文 ===RouteKey: /billing/invoicesTenantId: tenant-100UserId: 100Roles: Attorney=== 本页操作指南 ===Title: 发票列表操作Version: 3<最多 16000 字符 Markdown>这些数据会进入 AIManage 和最终 Provider 路径。TenantId/UserId 原值通常不是回答所需,Roles 也可能敏感;GA 前要用最小化 token 或抽象 Audience,而不是默认外发内部标识。
6. Prompt 注入与内容信任
指南由管理员编辑,但仍可能包含恶意/误写指令。Builder 只是把 Markdown 拼在 System Prompt 中,没有结构化隔离、内容签名、策略过滤或注入检测。模型也不应获得执行页面动作、读取 Secret 或访问任意工具的能力。
指南是产品权威内容,不代表其中每个字符串都可以成为高优先级系统指令。推荐把正文作为明确 delimit 的引用资料,并让安全策略与系统指令保持更高优先级。
7. 旧 Prompt 风险
Start 每次都会重新查询当前指南并构建 Prompt,但 Adapter 先查现有 Session;存在时直接返回,不更新 AIManage Conversation。于是指南 Revise/Delete、RequiredRole 变化、用户角色撤销或 Workspace 变更都不会刷新旧 Prompt。
角色被撤销的用户仍可用原 Session 继续问答,而旧 Conversation 可能保存之前的受限正文。这是 P0 安全缺口。Session 必须绑定 GuideRevision/PolicyVersion,并在每次使用时重验或被事件主动撤销。
8. 跨模块创建窗口
Start 先 CreateConversationAsync,再 sessions.AddAsync。两者属于不同模块/存储边界,没有显式事务或补偿。Session 插入失败会留下孤儿 Conversation。
两个并发 Start 都可能查不到 Session,分别创建 Conversation,然后在唯一索引竞争。当前没有 IdempotencyKey、唯一冲突重读、分布式锁或孤儿回收证据。
9. InitialQuestion 部分成功
Session 成功创建后才发送 InitialQuestion。若 AI 消息失败,Start Command 返回 Failure,但 Session 与 Conversation 已保留。客户端重试会复用它,可能可恢复,但响应没有 SessionCreatedButMessageFailed 状态或 SessionId。
GA 契约应返回清晰的阶段状态,或把首问作为带 ClientTurnId 的可重放子操作。不能用普通失败隐藏已经发生的持久化副作用。
10. 直接调用 AIManage Handler
Adapter 注入 ICommandHandler<SendMessage.Command,...> 与 IQueryHandler<StreamMessage.Command,...> 并直接调用 Handle,不是通过 Mediator Pipeline。这可能绕过 AIManage 请求级 Authorization、Feature、Validation、Audit 或其他 Pipeline 行为,具体取决于 Handler 内部自校验。
更稳健的设计是 AIManage owner 提供窄 IAiConversationSessionPort,内部统一执行 Workspace owner、Model allowlist、Usage、权限和幂等,而不是让消费模块选择具体 Store/Handler。
11. 消息与模型输入
Guidance 只检查 Content 非空,不限制长度、格式或敏感信息。ModelId 由客户端传入并原样交给 AIManage Handler;Guidance 没有租户允许模型、成本等级或数据区域校验。
助手独立 Feature/Permission/Quota 缺失。所有 Start/Send/Stream 仍使用 content Resource,UserId 缺失时写 "0",多个非用户调用者可能碰撞。
12. 会话目标合同
Session 建议保存 TenantId、UserId、Route、Component、Language、GuideId/Revision、PromptHash/PolicyVersion、Workspace/Model policy、Created/LastUsed/ExpiresAt、Status。每次消息验证所有权、Feature、角色/权限、revision 与过期状态。
指南发布/撤回、角色或 Permission 变化、Workspace 秘钥/策略轮换应发事件或执行在线重验,关闭/刷新受影响 Session。
13. 测试矩阵
| 场景 | 必须证明 |
|---|---|
| 助手关闭/缺 Workspace | 无 Conversation/Session |
| 相同 Start 重放 | 一个 Conversation 与 Session |
| 并发 Start | 唯一结果,无孤儿 |
| Session 保存失败 | Conversation 补偿/可恢复 |
| 首问失败 | 可见阶段状态,可安全重放 |
| 角色撤销 | 旧 Prompt 不可继续泄漏 |
| 指南发布新版本 | 刷新或新 Session |
| 跨租户/跨用户 SessionId | NotFound |
| 超长/敏感消息 | 写前拒绝/脱敏 |
| 未允许 ModelId | Policy denied |
14. 检查命令
# 当前 Prompt、会话唯一键和直接 AIManage 调用。rg -n "MaxGuideCharacters|TenantId:|UX_GuidanceAiSession|CreateConversationAsync|_sendMessageHandler.Handle" \ src/Platform/Guidance -g '*.cs'
# Prompt 版本、过期、关闭与助手权限当前预期无命中。rg -n "PromptHash|GuideVersion|ExpiresAt|CloseSession|guidance.assistant" \ src/Platform/Guidance -g '*.cs'Guidance 总览 · 上下文授权 · 流式与 GA