Guidance Assistant is not its own model implementation. Application assembles page context and calls the Guidance-owned IAiSessionAdapter. Infrastructure creates or reuses an AIManage conversation and persists a Guidance session mapping.
1. Enablement conditions
The adapter reads Guidance:EnableAiAssistant on every Start, Send, and Stream. Only a parseable true continues. Start also requires Guidance:AiWorkspaceId, with Guidance:WorkspaceId as a compatibility fallback.
{ "Guidance": { "EnableAiAssistant": true, "AiWorkspaceId": "workspace-guidance-prod" }}A missing flag returns Guidance.Assistant.Disabled; a missing workspace returns Guidance.Assistant.WorkspaceRequired. That is fail-closed behavior, not content fallback. Non-AI clients call Contextual separately.
2. Start sequence
The guide may be null and the assistant still creates a page/identity context conversation. Product policy should decide whether generic Q&A without an authoritative guide is allowed.
3. SessionId
GuidanceSessionId.Create joins trimmed route, component or (page), and language with |, hashes with SHA-256, Base64Url-encodes it, and prefixes guidance-{userId}-. TenantId is not in the hash, but entity lookup also binds tenant and user.
var grid = GuidanceSessionId.Create( userId, "/billing/invoices", "grid", LanguageCode.EnUs);var form = GuidanceSessionId.Create( userId, "/billing/invoices", "form", LanguageCode.EnUs);
// Raw route slashes are hidden and component prompts do not share a session.Debug.Assert(grid != form);Debug.Assert(!grid.Contains('/'));SessionId is deterministic, not an idempotency token. It exposes the numeric user ID in its prefix and omits tenant, prompt version, and guide version. Guess resistance is not authorization.
4. Session persistence
GuidanceAssistantSession is a TenantAggregateRoot<string>. Its unique index is (TenantId, UserId, SessionId), and it stores ConversationId, RouteKey, ComponentId, and CreateTime with soft-delete fields.
It does not store LanguageCode except indirectly in the hash, GuideId/version, PromptHash, WorkspaceId, ModelId, LastUsedAt, ExpiresAt, or close reason. There are no list, close, delete, or cleanup use cases.
5. Prompt contents
The builder starts with fixed Chinese system instructions, then appends RouteKey, ComponentId, LanguageCode, TenantId, UserId, and Roles. If a guide exists, it appends Title, ContentVersion, and up to 16,000 Markdown characters.
你是 BitzOrcas 法律 SaaS 的应用内页面向导助手。=== 当前上下文 ===RouteKey: /billing/invoicesTenantId: tenant-100UserId: 100Roles: Attorney=== 本页操作指南 ===Title: Invoice list operationsVersion: 3<up to 16000 Markdown characters>These values enter AIManage and the eventual provider path. Raw TenantId/UserId is usually unnecessary, and Roles may be sensitive. GA should send a minimized token or abstract audience rather than internal identifiers.
6. Prompt injection and content trust
Administrators author guides, but configuration remains capable of malicious or accidental instructions. The builder concatenates Markdown without structural isolation, signing, policy filtering, or injection detection. Models must not receive arbitrary page-action, secret, or tool privileges from that text.
Guide content is product authority, but not every string should become high-priority system instruction. Treat the body as delimited reference material under stronger system policy.
7. Stale-prompt risk
Start always fetches the current guide and builds a prompt, but the adapter returns an existing session without updating the AIManage conversation. Guide revision/deletion, RequiredRole changes, role revocation, and workspace changes therefore leave the old prompt intact.
A user whose role is revoked can keep asking through the old session, whose conversation may contain restricted Markdown. This is a P0 issue. Bind sessions to guide revision/policy version and revalidate each use or revoke by event.
8. Cross-module creation window
Start calls CreateConversationAsync before sessions.AddAsync. They span module/storage boundaries and have no explicit transaction or compensation. A failed session insert leaves an orphan conversation.
Two concurrent Starts may both miss the session, create separate conversations, then race on the session unique index. There is no IdempotencyKey, conflict reread, distributed lock, or orphan-reconciliation evidence.
9. InitialQuestion partial success
The session is created before sending InitialQuestion. If the message fails, Start returns failure but the session and conversation remain. A retry may reuse them, but the response does not expose a SessionCreatedButMessageFailed state or session ID.
GA should return stage-aware status or model the first question as a replayable ClientTurnId operation. A generic failure must not hide persistent side effects.
10. Direct AIManage handler invocation
The adapter injects concrete ICommandHandler<SendMessage.Command,...> and IQueryHandler<StreamMessage.Command,...> and calls Handle directly, not through the mediator pipeline. This can bypass AIManage request-level authorization, feature, validation, audit, or other behaviors unless the handler repeats them.
A safer design exposes an AIManage-owned narrow session port that centrally enforces workspace ownership, model allowlists, usage, permissions, and idempotency.
11. Message and model inputs
Guidance checks only nonblank Content. It has no message size, format, or sensitive-data bound. Client ModelId flows to the AIManage handler with no Guidance-level tenant allowlist, cost tier, or region check.
There is no assistant-specific permission, feature, or quota. Start/Send/Stream all reuse content Resource. Missing identity becomes user "0", allowing non-user callers to collide.
12. Target session contract
Persist tenant/user, route/component/language, guide ID/revision, prompt hash/policy, workspace/model policy, created/last-used/expiry, and status. Each message validates ownership, feature, role/permission, revision, and expiry.
Publication/revocation, role or permission changes, workspace credential/policy rotation should publish events or trigger online validation to close or refresh sessions.
13. Test matrix
| Scenario | Evidence required |
|---|---|
| disabled/missing workspace | no conversation/session |
| same Start replay | one conversation and session |
| concurrent Start | one result, no orphan |
| session insert failure | compensation/recovery |
| first-message failure | visible stage, safe replay |
| role revocation | old prompt cannot leak |
| new guide revision | refresh or new session |
| cross-tenant/user SessionId | not found |
| oversized/sensitive message | rejected/redacted before send |
| forbidden ModelId | policy denied |
14. Review commands
# Current prompt, session uniqueness, and direct AIManage calls.rg -n "MaxGuideCharacters|TenantId:|UX_GuidanceAiSession|CreateConversationAsync|_sendMessageHandler.Handle" \ src/Platform/Guidance -g '*.cs'
# Prompt version, expiry, close, and assistant permission should have no matches.rg -n "PromptHash|GuideVersion|ExpiresAt|CloseSession|guidance.assistant" \ src/Platform/Guidance -g '*.cs'Guidance overview · Contextual authorization · Streaming and GA