docs: freeze booking agent m0 contract
This commit is contained in:
parent
031d2db36e
commit
d0da10b35b
559
contracts/booking-agent-m0.openapi.yaml
Normal file
559
contracts/booking-agent-m0.openapi.yaml
Normal file
@ -0,0 +1,559 @@
|
||||
openapi: 3.0.3
|
||||
info:
|
||||
title: Petstore Booking Agent M0 API
|
||||
version: 0.1.0
|
||||
description: |
|
||||
智能预约 M0 冻结契约。M0 只生成并回填预约草稿,不注册 confirm endpoint,
|
||||
不直接创建 Appointment。除鉴权拦截器的 HTTP 401 外,当前 Petstore 客户端以
|
||||
响应体 code 作为业务结果;实现不得借本功能顺带重构全局响应语义。
|
||||
security:
|
||||
- bearerAuth: []
|
||||
x-contract-status: frozen-for-m0
|
||||
x-business-error-codes:
|
||||
- UNAUTHENTICATED
|
||||
- FORBIDDEN
|
||||
- AGENT_DISABLED
|
||||
- STORE_NOT_FOUND
|
||||
- SESSION_NOT_FOUND
|
||||
- SESSION_EXPIRED
|
||||
- SESSION_TERMINAL
|
||||
- DRAFT_VERSION_CONFLICT
|
||||
- INVALID_INPUT
|
||||
- INVALID_AUDIO
|
||||
- AUDIO_TOO_LARGE
|
||||
- RATE_LIMITED
|
||||
- AGENT_UNAVAILABLE
|
||||
- ASR_UNAVAILABLE
|
||||
paths:
|
||||
/api/booking-agent/sessions:
|
||||
post:
|
||||
operationId: createBookingAgentSession
|
||||
summary: 创建 customer 智能预约会话
|
||||
description: |
|
||||
仅已登录 customer。M0 要求先在普通预约页选定门店;entrySource 由服务端固定为
|
||||
appointment_create,不从客户端接收。
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/CreateSessionRequest'
|
||||
responses:
|
||||
'200':
|
||||
description: 成功或带业务 code 的可理解失败
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
oneOf:
|
||||
- $ref: '#/components/schemas/SessionEnvelope'
|
||||
- $ref: '#/components/schemas/ErrorEnvelope'
|
||||
'401':
|
||||
$ref: '#/components/responses/Unauthenticated'
|
||||
|
||||
/api/booking-agent/sessions/{sessionId}/messages:
|
||||
post:
|
||||
operationId: submitBookingAgentMessage
|
||||
summary: 提交一轮已确认文字并刷新草稿
|
||||
parameters:
|
||||
- $ref: '#/components/parameters/SessionId'
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/SubmitMessageRequest'
|
||||
responses:
|
||||
'200':
|
||||
description: 成功或带业务 code 的可理解失败
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
oneOf:
|
||||
- $ref: '#/components/schemas/SessionEnvelope'
|
||||
- $ref: '#/components/schemas/ErrorEnvelope'
|
||||
'401':
|
||||
$ref: '#/components/responses/Unauthenticated'
|
||||
|
||||
/api/booking-agent/sessions/{sessionId}/transcriptions:
|
||||
post:
|
||||
operationId: transcribeBookingAgentAudio
|
||||
summary: 转写当前 customer 会话的一段短音频
|
||||
description: |
|
||||
只做语音转写,不自动提交消息。客户端必须把返回文字放回可编辑输入框,用户确认后
|
||||
再调用 messages。后端从 session 解析门店和 customer 范围,不接受客户端上传词表。
|
||||
parameters:
|
||||
- $ref: '#/components/parameters/SessionId'
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
multipart/form-data:
|
||||
schema:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
required:
|
||||
- audio
|
||||
properties:
|
||||
audio:
|
||||
type: string
|
||||
format: binary
|
||||
description: 单段不超过 60 秒、3 MB;MIME 与真实文件头必须一致
|
||||
responses:
|
||||
'200':
|
||||
description: 成功或带业务 code 的可理解失败
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
oneOf:
|
||||
- $ref: '#/components/schemas/TranscriptionEnvelope'
|
||||
- $ref: '#/components/schemas/ErrorEnvelope'
|
||||
'401':
|
||||
$ref: '#/components/responses/Unauthenticated'
|
||||
|
||||
/api/booking-agent/sessions/{sessionId}/fallback:
|
||||
post:
|
||||
operationId: handoffBookingAgentDraft
|
||||
summary: 结束助手并返回可回填普通表单的已验证草稿
|
||||
parameters:
|
||||
- $ref: '#/components/parameters/SessionId'
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/DraftVersionRequest'
|
||||
responses:
|
||||
'200':
|
||||
description: 成功或带业务 code 的可理解失败
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
oneOf:
|
||||
- $ref: '#/components/schemas/HandoffEnvelope'
|
||||
- $ref: '#/components/schemas/ErrorEnvelope'
|
||||
'401':
|
||||
$ref: '#/components/responses/Unauthenticated'
|
||||
|
||||
/api/booking-agent/sessions/{sessionId}:
|
||||
delete:
|
||||
operationId: cancelBookingAgentSession
|
||||
summary: 结束本人智能预约会话
|
||||
parameters:
|
||||
- $ref: '#/components/parameters/SessionId'
|
||||
responses:
|
||||
'200':
|
||||
description: 成功或带业务 code 的可理解失败
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
oneOf:
|
||||
- $ref: '#/components/schemas/CancelEnvelope'
|
||||
- $ref: '#/components/schemas/ErrorEnvelope'
|
||||
'401':
|
||||
$ref: '#/components/responses/Unauthenticated'
|
||||
|
||||
components:
|
||||
securitySchemes:
|
||||
bearerAuth:
|
||||
type: http
|
||||
scheme: bearer
|
||||
bearerFormat: PetstoreSessionToken
|
||||
|
||||
parameters:
|
||||
SessionId:
|
||||
name: sessionId
|
||||
in: path
|
||||
required: true
|
||||
description: 不可预测的 UUID;仍必须和 current.userId 联合查询
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
|
||||
responses:
|
||||
Unauthenticated:
|
||||
description: session token 缺失、失效或账号权限已变化
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorEnvelope'
|
||||
example:
|
||||
code: 401
|
||||
message: 登录已失效,请重新登录
|
||||
bizCode: UNAUTHENTICATED
|
||||
|
||||
schemas:
|
||||
CreateSessionRequest:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
required:
|
||||
- storeId
|
||||
properties:
|
||||
storeId:
|
||||
type: integer
|
||||
format: int64
|
||||
minimum: 1
|
||||
|
||||
SubmitMessageRequest:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
required:
|
||||
- inputType
|
||||
- text
|
||||
- draftVersion
|
||||
properties:
|
||||
inputType:
|
||||
type: string
|
||||
enum:
|
||||
- text
|
||||
- voice
|
||||
text:
|
||||
type: string
|
||||
minLength: 1
|
||||
maxLength: 500
|
||||
description: 用户确认后的当轮输入;服务端不得写日志或长期持久化
|
||||
draftVersion:
|
||||
type: integer
|
||||
minimum: 0
|
||||
|
||||
DraftVersionRequest:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
required:
|
||||
- draftVersion
|
||||
properties:
|
||||
draftVersion:
|
||||
type: integer
|
||||
minimum: 0
|
||||
|
||||
BookingAgentStatus:
|
||||
type: string
|
||||
enum:
|
||||
- collecting
|
||||
- proposing
|
||||
- confirmable
|
||||
- fallback
|
||||
- expired
|
||||
- cancelled
|
||||
|
||||
MissingField:
|
||||
type: string
|
||||
enum:
|
||||
- pet
|
||||
- service
|
||||
- date
|
||||
- time
|
||||
|
||||
BookingDraft:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
required:
|
||||
- storeId
|
||||
- storeName
|
||||
- petId
|
||||
- petName
|
||||
- petType
|
||||
- serviceTypeId
|
||||
- serviceType
|
||||
- durationMinutes
|
||||
- dateConstraint
|
||||
- timeWindow
|
||||
- appointmentTime
|
||||
- appointmentEndTime
|
||||
- remark
|
||||
- missingFields
|
||||
properties:
|
||||
storeId:
|
||||
type: integer
|
||||
format: int64
|
||||
storeName:
|
||||
type: string
|
||||
maxLength: 128
|
||||
petId:
|
||||
type: integer
|
||||
format: int64
|
||||
nullable: true
|
||||
petName:
|
||||
type: string
|
||||
nullable: true
|
||||
maxLength: 64
|
||||
petType:
|
||||
type: string
|
||||
nullable: true
|
||||
maxLength: 32
|
||||
serviceTypeId:
|
||||
type: integer
|
||||
format: int64
|
||||
nullable: true
|
||||
serviceType:
|
||||
type: string
|
||||
nullable: true
|
||||
maxLength: 64
|
||||
durationMinutes:
|
||||
type: integer
|
||||
nullable: true
|
||||
minimum: 1
|
||||
dateConstraint:
|
||||
type: string
|
||||
format: date
|
||||
nullable: true
|
||||
timeWindow:
|
||||
$ref: '#/components/schemas/TimeWindow'
|
||||
appointmentTime:
|
||||
$ref: '#/components/schemas/NullableLocalDateTime'
|
||||
appointmentEndTime:
|
||||
$ref: '#/components/schemas/NullableLocalDateTime'
|
||||
remark:
|
||||
type: string
|
||||
nullable: true
|
||||
maxLength: 200
|
||||
missingFields:
|
||||
type: array
|
||||
uniqueItems: true
|
||||
items:
|
||||
$ref: '#/components/schemas/MissingField'
|
||||
|
||||
TimeWindow:
|
||||
type: object
|
||||
nullable: true
|
||||
additionalProperties: false
|
||||
required:
|
||||
- start
|
||||
- end
|
||||
properties:
|
||||
start:
|
||||
type: string
|
||||
nullable: true
|
||||
pattern: '^(?:[01]\d|2[0-3]):[0-5]\d$'
|
||||
end:
|
||||
type: string
|
||||
nullable: true
|
||||
pattern: '^(?:[01]\d|2[0-3]):[0-5]\d$'
|
||||
|
||||
SlotOption:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
required:
|
||||
- startTime
|
||||
- endTime
|
||||
- label
|
||||
properties:
|
||||
startTime:
|
||||
$ref: '#/components/schemas/LocalDateTime'
|
||||
endTime:
|
||||
$ref: '#/components/schemas/LocalDateTime'
|
||||
label:
|
||||
type: string
|
||||
maxLength: 64
|
||||
|
||||
QuickReply:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
required:
|
||||
- type
|
||||
- value
|
||||
- label
|
||||
properties:
|
||||
type:
|
||||
type: string
|
||||
enum:
|
||||
- pet
|
||||
- service
|
||||
- slot
|
||||
- date
|
||||
- fallback
|
||||
value:
|
||||
type: string
|
||||
maxLength: 128
|
||||
label:
|
||||
type: string
|
||||
maxLength: 64
|
||||
|
||||
SessionView:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
required:
|
||||
- sessionId
|
||||
- status
|
||||
- assistantMessage
|
||||
- draft
|
||||
- slotOptions
|
||||
- quickReplies
|
||||
- confirmable
|
||||
- draftVersion
|
||||
- expiresAt
|
||||
properties:
|
||||
sessionId:
|
||||
type: string
|
||||
format: uuid
|
||||
status:
|
||||
$ref: '#/components/schemas/BookingAgentStatus'
|
||||
assistantMessage:
|
||||
type: string
|
||||
maxLength: 300
|
||||
description: 服务端模板渲染,不包含模型思考或供应商错误
|
||||
draft:
|
||||
$ref: '#/components/schemas/BookingDraft'
|
||||
slotOptions:
|
||||
type: array
|
||||
maxItems: 3
|
||||
items:
|
||||
$ref: '#/components/schemas/SlotOption'
|
||||
quickReplies:
|
||||
type: array
|
||||
maxItems: 10
|
||||
items:
|
||||
$ref: '#/components/schemas/QuickReply'
|
||||
confirmable:
|
||||
type: boolean
|
||||
draftVersion:
|
||||
type: integer
|
||||
minimum: 0
|
||||
expiresAt:
|
||||
$ref: '#/components/schemas/LocalDateTime'
|
||||
|
||||
LocalDateTime:
|
||||
type: string
|
||||
pattern: '^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}$'
|
||||
description: Asia/Shanghai 下的本地日期时间,格式 yyyy-MM-ddTHH:mm:ss
|
||||
|
||||
NullableLocalDateTime:
|
||||
type: string
|
||||
nullable: true
|
||||
pattern: '^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}$'
|
||||
description: null 或 Asia/Shanghai 下的本地日期时间;非空时格式为 yyyy-MM-ddTHH:mm:ss
|
||||
|
||||
HandoffData:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
required:
|
||||
- source
|
||||
- draft
|
||||
properties:
|
||||
source:
|
||||
type: string
|
||||
enum:
|
||||
- booking-agent-m0
|
||||
draft:
|
||||
$ref: '#/components/schemas/BookingDraft'
|
||||
|
||||
TranscriptionData:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
required:
|
||||
- text
|
||||
properties:
|
||||
text:
|
||||
type: string
|
||||
maxLength: 500
|
||||
description: 可编辑转写;不返回供应商原始响应或外部 request ID
|
||||
|
||||
CancelData:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
required:
|
||||
- sessionId
|
||||
- status
|
||||
properties:
|
||||
sessionId:
|
||||
type: string
|
||||
format: uuid
|
||||
status:
|
||||
type: string
|
||||
enum:
|
||||
- cancelled
|
||||
|
||||
SessionEnvelope:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
required:
|
||||
- code
|
||||
- data
|
||||
properties:
|
||||
code:
|
||||
type: integer
|
||||
enum:
|
||||
- 200
|
||||
data:
|
||||
$ref: '#/components/schemas/SessionView'
|
||||
|
||||
TranscriptionEnvelope:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
required:
|
||||
- code
|
||||
- data
|
||||
properties:
|
||||
code:
|
||||
type: integer
|
||||
enum:
|
||||
- 200
|
||||
data:
|
||||
$ref: '#/components/schemas/TranscriptionData'
|
||||
|
||||
HandoffEnvelope:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
required:
|
||||
- code
|
||||
- data
|
||||
properties:
|
||||
code:
|
||||
type: integer
|
||||
enum:
|
||||
- 200
|
||||
data:
|
||||
$ref: '#/components/schemas/HandoffData'
|
||||
|
||||
CancelEnvelope:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
required:
|
||||
- code
|
||||
- data
|
||||
properties:
|
||||
code:
|
||||
type: integer
|
||||
enum:
|
||||
- 200
|
||||
data:
|
||||
$ref: '#/components/schemas/CancelData'
|
||||
|
||||
ErrorEnvelope:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
required:
|
||||
- code
|
||||
- message
|
||||
- bizCode
|
||||
properties:
|
||||
code:
|
||||
type: integer
|
||||
enum:
|
||||
- 400
|
||||
- 401
|
||||
- 403
|
||||
- 404
|
||||
- 409
|
||||
- 410
|
||||
- 413
|
||||
- 429
|
||||
- 503
|
||||
message:
|
||||
type: string
|
||||
maxLength: 200
|
||||
bizCode:
|
||||
type: string
|
||||
enum:
|
||||
- UNAUTHENTICATED
|
||||
- FORBIDDEN
|
||||
- AGENT_DISABLED
|
||||
- STORE_NOT_FOUND
|
||||
- SESSION_NOT_FOUND
|
||||
- SESSION_EXPIRED
|
||||
- SESSION_TERMINAL
|
||||
- DRAFT_VERSION_CONFLICT
|
||||
- INVALID_INPUT
|
||||
- INVALID_AUDIO
|
||||
- AUDIO_TOO_LARGE
|
||||
- RATE_LIMITED
|
||||
- AGENT_UNAVAILABLE
|
||||
- ASR_UNAVAILABLE
|
||||
138
contracts/booking-intent-v1.schema.json
Normal file
138
contracts/booking-intent-v1.schema.json
Normal file
@ -0,0 +1,138 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "https://petstore.internal/schemas/booking-intent-v1.schema.json",
|
||||
"title": "BookingIntentV1",
|
||||
"description": "智能预约 M0 模型输出。所有字段仅是非权威意图提示,业务 ID、服务事实和号源事实由 Petstore 后端重新解析。",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"schemaVersion",
|
||||
"intent",
|
||||
"petQuery",
|
||||
"serviceQuery",
|
||||
"dateExpression",
|
||||
"timeWindow",
|
||||
"remark",
|
||||
"clearFields",
|
||||
"ambiguities",
|
||||
"nextAction"
|
||||
],
|
||||
"properties": {
|
||||
"schemaVersion": {
|
||||
"const": "booking-intent-v1"
|
||||
},
|
||||
"intent": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"book",
|
||||
"modify",
|
||||
"end",
|
||||
"fallback"
|
||||
]
|
||||
},
|
||||
"petQuery": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"minLength": 1,
|
||||
"maxLength": 64
|
||||
},
|
||||
"serviceQuery": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"minLength": 1,
|
||||
"maxLength": 64
|
||||
},
|
||||
"dateExpression": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"minLength": 1,
|
||||
"maxLength": 32
|
||||
},
|
||||
"timeWindow": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "null"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"start",
|
||||
"end"
|
||||
],
|
||||
"properties": {
|
||||
"start": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"pattern": "^(?:[01]\\d|2[0-3]):[0-5]\\d$"
|
||||
},
|
||||
"end": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"pattern": "^(?:[01]\\d|2[0-3]):[0-5]\\d$"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"remark": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"minLength": 1,
|
||||
"maxLength": 200
|
||||
},
|
||||
"clearFields": {
|
||||
"type": "array",
|
||||
"uniqueItems": true,
|
||||
"maxItems": 5,
|
||||
"items": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"petQuery",
|
||||
"serviceQuery",
|
||||
"dateExpression",
|
||||
"timeWindow",
|
||||
"remark"
|
||||
]
|
||||
}
|
||||
},
|
||||
"ambiguities": {
|
||||
"type": "array",
|
||||
"uniqueItems": true,
|
||||
"maxItems": 5,
|
||||
"items": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"pet",
|
||||
"service",
|
||||
"date",
|
||||
"time",
|
||||
"remark"
|
||||
]
|
||||
}
|
||||
},
|
||||
"nextAction": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"ask",
|
||||
"resolve_context",
|
||||
"search_slots",
|
||||
"show_draft",
|
||||
"fallback",
|
||||
"end"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3,7 +3,7 @@
|
||||
> 本体条目 ↔ 文档章节 ↔ 代码路径 ↔ 测试用例 三方对齐审计。
|
||||
> 证据强度:`anchored`(三处对齐)/ `documented`(仅文档)/ `implemented`(仅代码)/ `gap`(缺失)。
|
||||
>
|
||||
> **刷新时间**:2026-08-02(FollowUpTask 回访闭环收口后刷新)
|
||||
> **刷新时间**:2026-08-02(智能预约 M0 Batch 0 契约冻结后刷新)
|
||||
|
||||
## 审计方法
|
||||
|
||||
@ -18,16 +18,16 @@
|
||||
|
||||
| 类型 | 数量 |
|
||||
|------|------|
|
||||
| Entity | 29 |
|
||||
| Action | 75 |
|
||||
| Event | 33 |
|
||||
| Rule | 49 |
|
||||
| Relation | 102 |
|
||||
| **合计** | **288** |
|
||||
| Entity | 30 |
|
||||
| Action | 80 |
|
||||
| Event | 36 |
|
||||
| Rule | 55 |
|
||||
| Relation | 112 |
|
||||
| **合计** | **313** |
|
||||
|
||||
证据强度:`anchored` 249(86.5%)/ `documented` 28 / `gap` 9 / `implemented` 2。
|
||||
证据强度:`anchored` 250(79.9%)/ `documented` 52 / `gap` 9 / `implemented` 2。
|
||||
|
||||
> anchored 占比相对 Batch 2 下降,是因为主动录入了 P1/P2 `gap` 占位与客户端埋点 `documented` 条目;端点漂移仍为 0。
|
||||
> anchored 占比相对早期快照下降,是因为主动录入了 P1/P2 `gap`、客户端埋点和智能预约 Batch 0 `documented` 条目;端点漂移仍为 0。
|
||||
|
||||
校验:
|
||||
|
||||
@ -36,7 +36,7 @@ python3 docs/graph/validate_ontology.py docs
|
||||
python3 docs/graph/audit_drift.py
|
||||
```
|
||||
|
||||
当前结果:校验通过;HTTP 端点 68/68、JPA Entity 16/16 对齐;非 JPA 领域实体 13 个;代码与本体均无单边漂移。
|
||||
当前结果:校验通过;HTTP 端点 68/68、JPA Entity 16/16 对齐;非 JPA/计划领域实体 14 个;代码与本体均无单边漂移。智能预约五个未实现端点以 `planned METHOD /path` 建模,不冒充当前代码端点。
|
||||
|
||||
## 元数据卫生
|
||||
|
||||
@ -72,9 +72,10 @@ python3 docs/graph/audit_drift.py
|
||||
| customer_timeline | ✅ | ✅ read model | StoreCustomerTimelineServiceTest | anchored | StoreCustomer + BusinessEvent 的同店低敏事实投影;jpa:false |
|
||||
| highlight_fail_reason | ✅ | ✅ 枚举 | ReportHighlightVideoServiceTest | anchored | jpa:false |
|
||||
| current_user | ✅ | ✅ record | SessionTokenServiceTest | anchored | jpa:false |
|
||||
| booking_agent_session | ✅ | —(Batch 0 设计) | — | documented | M0 短期会话;实现批创建 JPA/迁移/测试后才可改 anchored |
|
||||
| membership 等 5 个 | 方向库 | — | — | gap | roadmap 占位 |
|
||||
|
||||
## 规则覆盖度(49 条)
|
||||
## 规则覆盖度(55 条)
|
||||
|
||||
| 规则 | 测试 | 证据 |
|
||||
|------|------|------|
|
||||
@ -101,6 +102,7 @@ python3 docs/graph/audit_drift.py
|
||||
| BR-AUDIT-001 | AuditLogServiceTest / StoreOnboardingMigrationTest | anchored |
|
||||
| BR-SC-002 客户时间线低敏投影 | StoreCustomerTimelineServiceTest / AdminServiceCustomerControllerTest | anchored |
|
||||
| BR-FU-001/002 回访状态机与真实再次预约 | FollowUpTaskServiceTest / AppointmentServiceTest / MigrationTest | anchored |
|
||||
| BR-BA-001..005 智能预约 M0 边界 | 实现前;ADR + OpenAPI + JSON Schema 已冻结 | documented |
|
||||
|
||||
## 待补 gap 清单
|
||||
|
||||
@ -118,6 +120,7 @@ python3 docs/graph/audit_drift.py
|
||||
| 低 | 成片真 FFmpeg 端到端集成测(本机有 ffmpeg) | 🟡 单测已覆盖状态机与归类 |
|
||||
| 低 | `action:logout` / `event:session_revoked` 服务端吊销 | 🟡 gap |
|
||||
| 低 | 废弃 `register_store` / wechat OAuth demo 代码路径 | 🟡 deprecated 已标 |
|
||||
| 高 | 智能预约 M0 会话、五个 endpoint、三个事件和五条规则实现证据 | 🟡 documented / Ready;代码+测试同批才转 anchored |
|
||||
| — | 会员/储值/套餐/历史报告/看板 | gap 占位,不展开 |
|
||||
|
||||
## P1/P2 方向(jsonl 已占位)
|
||||
@ -144,4 +147,16 @@ python3 docs/graph/audit_drift.py
|
||||
|
||||
**页面清单 PRD(已写)**:`docs/门店管理后台-PhaseA-页面清单PRD.md`(A0–A7 全页字段/筛选/API/验收)。
|
||||
|
||||
当前 validate 规模以脚本输出为准(288 条目)。
|
||||
当前 validate 规模以脚本输出为准(313 条目)。
|
||||
|
||||
## 智能预约 M0 Batch 0 状态(2026-08-02)
|
||||
|
||||
| 类型 | 已冻结 ID | 证据 |
|
||||
|------|------|------|
|
||||
| Entity | `entity:booking_agent_session` | documented |
|
||||
| Action | `create/submit/transcribe/handoff/cancel_booking_agent_*` 共 5 条 | documented |
|
||||
| Event | `booking_agent_started/draft_ready/fallback` | documented |
|
||||
| Rule | `BR-BA-001..005` | documented |
|
||||
| Relation | owner/store/session 读写与三条 EMITS 共 10 条 | documented |
|
||||
|
||||
契约评审已通过,尚未实现。下一实现批必须以真实 code/test 路径替换空证据,并将 `BookingAgentSession` 从计划对象切换为真实 JPA 实体;禁止仅修改 `evidence` 文字。
|
||||
|
||||
@ -287,3 +287,27 @@
|
||||
{"id":"rel:create_appointment_emits_rebook_created","type":"Relation","subject":"action:create_appointment","predicate":"EMITS","object":"event:rebook_created","domain":"lead","evidence":"anchored"}
|
||||
{"id":"rel:br_fu_001_applies_follow_up","type":"Relation","subject":"rule:BR-FU-001","predicate":"APPLIES_TO","object":"action:start_follow_up_task","domain":"lead","evidence":"anchored"}
|
||||
{"id":"rel:br_fu_002_applies_create_appointment","type":"Relation","subject":"rule:BR-FU-002","predicate":"APPLIES_TO","object":"action:create_appointment","domain":"lead","evidence":"anchored"}
|
||||
{"id":"entity:booking_agent_session","type":"Entity","name":"BookingAgentSession","nameZh":"智能预约短期会话","domain":"booking_agent","jpa":false,"fields":["id","session_id","customer_user_id","store_id","status","draft_json","draft_version","entry_source","input_modality","expires_at","create_time","update_time"],"systems":["Backend Core","Customer Experience FE"],"code":"","doc":"docs/ontology/objects.md#entity:booking_agent_session; docs/架构决策-智能预约助手M0契约与数据模型-2026-08-02.md","test":"","evidence":"documented","status":"planned","note":"Batch 0 冻结设计;尚未创建 JPA 实体,实现批需将 jpa 与证据切换为真实状态"}
|
||||
{"id":"action:create_booking_agent_session","type":"Action","name":"createBookingAgentSession","nameZh":"创建智能预约会话","domain":"booking_agent","inputs":["storeId","entity:current_user"],"outputs":["entity:booking_agent_session","event:booking_agent_started"],"entrypoints":["planned POST /api/booking-agent/sessions"],"appliesRules":["rule:BR-AUTH-001","rule:BR-BA-001","rule:BR-BA-003","rule:BR-BA-005","rule:BR-BE-001"],"code":"","doc":"docs/ontology/actions.md#action:create_booking_agent_session; docs/架构决策-智能预约助手M0契约与数据模型-2026-08-02.md","test":"","evidence":"documented","status":"planned"}
|
||||
{"id":"action:submit_booking_agent_message","type":"Action","name":"submitBookingAgentMessage","nameZh":"提交智能预约消息","domain":"booking_agent","inputs":["entity:booking_agent_session","entity:current_user","draftVersion","inputType","text"],"outputs":["entity:booking_agent_session","event:booking_agent_draft_ready"],"entrypoints":["planned POST /api/booking-agent/sessions/{sessionId}/messages"],"appliesRules":["rule:BR-AUTH-001","rule:BR-BA-001","rule:BR-BA-002","rule:BR-BA-003","rule:BR-BA-004","rule:BR-BA-005","rule:BR-BE-001"],"code":"","doc":"docs/ontology/actions.md#action:submit_booking_agent_message; docs/架构决策-智能预约助手M0契约与数据模型-2026-08-02.md","test":"","evidence":"documented","status":"planned"}
|
||||
{"id":"action:transcribe_booking_agent_audio","type":"Action","name":"transcribeBookingAgentAudio","nameZh":"转写智能预约语音","domain":"booking_agent","inputs":["entity:booking_agent_session","entity:current_user","audio"],"outputs":["editableText"],"entrypoints":["planned POST /api/booking-agent/sessions/{sessionId}/transcriptions"],"appliesRules":["rule:BR-AUTH-001","rule:BR-BA-001","rule:BR-BA-004","rule:BR-BA-005"],"code":"","doc":"docs/ontology/actions.md#action:transcribe_booking_agent_audio; docs/架构决策-智能预约助手M0契约与数据模型-2026-08-02.md","test":"","evidence":"documented","status":"planned"}
|
||||
{"id":"action:handoff_booking_agent_draft","type":"Action","name":"handoffBookingAgentDraft","nameZh":"交接智能预约草稿","domain":"booking_agent","inputs":["entity:booking_agent_session","entity:current_user","draftVersion"],"outputs":["entity:booking_agent_session","event:booking_agent_fallback"],"entrypoints":["planned POST /api/booking-agent/sessions/{sessionId}/fallback"],"appliesRules":["rule:BR-AUTH-001","rule:BR-BA-001","rule:BR-BA-003","rule:BR-BA-004","rule:BR-BA-005","rule:BR-BE-001"],"code":"","doc":"docs/ontology/actions.md#action:handoff_booking_agent_draft; docs/架构决策-智能预约助手M0契约与数据模型-2026-08-02.md","test":"","evidence":"documented","status":"planned"}
|
||||
{"id":"action:cancel_booking_agent_session","type":"Action","name":"cancelBookingAgentSession","nameZh":"结束智能预约会话","domain":"booking_agent","inputs":["entity:booking_agent_session","entity:current_user"],"outputs":["entity:booking_agent_session"],"entrypoints":["planned DELETE /api/booking-agent/sessions/{sessionId}"],"appliesRules":["rule:BR-AUTH-001","rule:BR-BA-001","rule:BR-BA-003"],"code":"","doc":"docs/ontology/actions.md#action:cancel_booking_agent_session; docs/架构决策-智能预约助手M0契约与数据模型-2026-08-02.md","test":"","evidence":"documented","status":"planned"}
|
||||
{"id":"event:booking_agent_started","type":"Event","name":"BookingAgentStarted","nameZh":"智能预约会话已启动","domain":"booking_agent","payload":["storeId","aggregateType","aggregateId","actorUserId","actorRole","source","entrySource","occurredAt"],"emittedBy":["action:create_booking_agent_session"],"appliesRules":["rule:BR-BA-001","rule:BR-BA-004","rule:BR-BE-001"],"code":"","doc":"docs/ontology/events.md#event:booking_agent_started; docs/架构决策-智能预约助手M0契约与数据模型-2026-08-02.md","test":"","evidence":"documented","status":"planned"}
|
||||
{"id":"event:booking_agent_draft_ready","type":"Event","name":"BookingAgentDraftReady","nameZh":"智能预约草稿已就绪","domain":"booking_agent","payload":["storeId","aggregateType","aggregateId","actorUserId","actorRole","source","inputModality","occurredAt"],"emittedBy":["action:submit_booking_agent_message"],"appliesRules":["rule:BR-BA-002","rule:BR-BA-003","rule:BR-BA-004","rule:BR-BE-001"],"code":"","doc":"docs/ontology/events.md#event:booking_agent_draft_ready; docs/架构决策-智能预约助手M0契约与数据模型-2026-08-02.md","test":"","evidence":"documented","status":"planned"}
|
||||
{"id":"event:booking_agent_fallback","type":"Event","name":"BookingAgentFallback","nameZh":"智能预约已降级交接","domain":"booking_agent","payload":["storeId","aggregateType","aggregateId","actorUserId","actorRole","source","reason","occurredAt"],"emittedBy":["action:handoff_booking_agent_draft"],"appliesRules":["rule:BR-BA-003","rule:BR-BA-004","rule:BR-BA-005","rule:BR-BE-001"],"code":"","doc":"docs/ontology/events.md#event:booking_agent_fallback; docs/架构决策-智能预约助手M0契约与数据模型-2026-08-02.md","test":"","evidence":"documented","status":"planned"}
|
||||
{"id":"rule:BR-BA-001","type":"Rule","name":"bookingAgentCustomerStoreBoundary","nameZh":"智能预约 customer 与门店边界","domain":"booking_agent","appliesTo":["action:create_booking_agent_session","action:submit_booking_agent_message","action:transcribe_booking_agent_audio","action:handoff_booking_agent_draft","action:cancel_booking_agent_session"],"code":"","doc":"docs/ontology/rules.md#rule:BR-BA-001; docs/架构决策-智能预约助手M0契约与数据模型-2026-08-02.md","test":"","evidence":"documented","status":"planned"}
|
||||
{"id":"rule:BR-BA-002","type":"Rule","name":"bookingAgentModelNonAuthority","nameZh":"模型非权威与结构强校验","domain":"booking_agent","appliesTo":["action:submit_booking_agent_message"],"code":"","doc":"docs/ontology/rules.md#rule:BR-BA-002; docs/contracts/booking-intent-v1.schema.json; docs/架构决策-智能预约助手M0契约与数据模型-2026-08-02.md","test":"","evidence":"documented","status":"planned"}
|
||||
{"id":"rule:BR-BA-003","type":"Rule","name":"bookingAgentStateVersionTtlNoWrite","nameZh":"会话状态版本 TTL 与无写路径","domain":"booking_agent","appliesTo":["action:create_booking_agent_session","action:submit_booking_agent_message","action:handoff_booking_agent_draft","action:cancel_booking_agent_session"],"code":"","doc":"docs/ontology/rules.md#rule:BR-BA-003; docs/架构决策-智能预约助手M0契约与数据模型-2026-08-02.md","test":"","evidence":"documented","status":"planned"}
|
||||
{"id":"rule:BR-BA-004","type":"Rule","name":"bookingAgentPrivacyMinimization","nameZh":"语音原文与日志最小化","domain":"booking_agent","appliesTo":["action:submit_booking_agent_message","action:transcribe_booking_agent_audio","action:handoff_booking_agent_draft"],"code":"","doc":"docs/ontology/rules.md#rule:BR-BA-004; docs/架构决策-智能预约助手M0契约与数据模型-2026-08-02.md","test":"","evidence":"documented","status":"planned"}
|
||||
{"id":"rule:BR-BA-005","type":"Rule","name":"bookingAgentDisabledRateLimitedFallback","nameZh":"默认关闭限流与可退化","domain":"booking_agent","appliesTo":["action:create_booking_agent_session","action:submit_booking_agent_message","action:transcribe_booking_agent_audio","action:handoff_booking_agent_draft"],"code":"","doc":"docs/ontology/rules.md#rule:BR-BA-005; docs/架构决策-智能预约助手M0契约与数据模型-2026-08-02.md","test":"","evidence":"documented","status":"planned"}
|
||||
{"id":"rel:user_has_booking_agent_session","type":"Relation","subject":"entity:user","predicate":"HAS","object":"entity:booking_agent_session","domain":"booking_agent","evidence":"documented"}
|
||||
{"id":"rel:store_has_booking_agent_session","type":"Relation","subject":"entity:store","predicate":"HAS","object":"entity:booking_agent_session","domain":"booking_agent","evidence":"documented"}
|
||||
{"id":"rel:create_booking_agent_session_creates_session","type":"Relation","subject":"action:create_booking_agent_session","predicate":"CREATES","object":"entity:booking_agent_session","domain":"booking_agent","evidence":"documented"}
|
||||
{"id":"rel:submit_booking_agent_message_updates_session","type":"Relation","subject":"action:submit_booking_agent_message","predicate":"UPDATES","object":"entity:booking_agent_session","domain":"booking_agent","evidence":"documented"}
|
||||
{"id":"rel:transcribe_booking_agent_audio_reads_session","type":"Relation","subject":"action:transcribe_booking_agent_audio","predicate":"READS","object":"entity:booking_agent_session","domain":"booking_agent","evidence":"documented"}
|
||||
{"id":"rel:handoff_booking_agent_draft_updates_session","type":"Relation","subject":"action:handoff_booking_agent_draft","predicate":"UPDATES","object":"entity:booking_agent_session","domain":"booking_agent","evidence":"documented"}
|
||||
{"id":"rel:cancel_booking_agent_session_updates_session","type":"Relation","subject":"action:cancel_booking_agent_session","predicate":"UPDATES","object":"entity:booking_agent_session","domain":"booking_agent","evidence":"documented"}
|
||||
{"id":"rel:create_booking_agent_session_emits_started","type":"Relation","subject":"action:create_booking_agent_session","predicate":"EMITS","object":"event:booking_agent_started","domain":"booking_agent","evidence":"documented"}
|
||||
{"id":"rel:submit_booking_agent_message_emits_draft_ready","type":"Relation","subject":"action:submit_booking_agent_message","predicate":"EMITS","object":"event:booking_agent_draft_ready","domain":"booking_agent","evidence":"documented"}
|
||||
{"id":"rel:handoff_booking_agent_draft_emits_fallback","type":"Relation","subject":"action:handoff_booking_agent_draft","predicate":"EMITS","object":"event:booking_agent_fallback","domain":"booking_agent","evidence":"documented"}
|
||||
|
||||
@ -61,6 +61,8 @@ rel:subject_predicate_object # rel:report_belongs_to_appointment
|
||||
|
||||
已覆盖 P0 主闭环实体与动作,并补录:`Pet`、`ServiceType`、`ScheduleBlock`、`HighlightVideo`、`SessionToken`、`ReportImage`、`ReportTestimonial`、`ServiceInterval`、`HighlightFailReason`、`CurrentUser`、`FollowUpTask`,以及成片/上传/鉴权/寄语/退订/回访状态机/真实再次预约相关规则。
|
||||
|
||||
**智能预约助手 M0** 已以 `documented` 证据录入 `BookingAgentSession`、5 个动作、3 个漏斗事件、`BR-BA-001..005` 和 10 条关系。它们表示 Batch 0 契约已冻结,**不表示代码已实现**;五个计划 endpoint 以 `planned METHOD /path` 录入,实现时必须与代码、测试同批转为 `anchored`。
|
||||
|
||||
**门店 Web 后台 Phase A**:`AdminConsole`、`Workbench`、`WorkbenchTodoItem` 仍是非 JPA 读模型;`StoreCustomer` 与 `FollowUpTask` 已落为 JPA 稳定业务对象。`BR-ADMIN-001`…`004`、`BR-FU-001/002` 与 admin actions 继续约束后台;收银/会员资产仍为 gap。
|
||||
|
||||
P1/P2 方向(会员/储值/套餐/历史报告/看板)以 `gap` 证据条目录入,不展开字段;受 `rule:BR-ADMIN-004` 约束不得偷渡进 Phase A 设计。
|
||||
|
||||
@ -715,3 +715,52 @@ legacy 永久邀请码查询入口已停用,固定返回 `410 / LEGACY_INVITE_
|
||||
- **入口**:Admin UI `/schedule`;`GET /api/schedule/day`;`POST/DELETE /api/schedule/block`
|
||||
- **适用规则**:`rule:BR-ADMIN-001`、`rule:BR-ADMIN-002`、`rule:BR-SCH-001`
|
||||
- **证据**:`anchored`
|
||||
|
||||
## 智能预约助手 M0(Batch 0 · documented)
|
||||
|
||||
> 下列入口已冻结但尚未注册;机器图以 `planned METHOD /path` 标识,不得误报为已实现 endpoint。
|
||||
|
||||
### action:create_booking_agent_session
|
||||
|
||||
使用已选有效 `storeId` 为当前 customer 创建短期会话,并首次记录 `booking_agent_started`。
|
||||
|
||||
- **计划入口**:`POST /api/booking-agent/sessions`
|
||||
- **输出**:`entity:booking_agent_session`
|
||||
- **规则**:`rule:BR-AUTH-001`、`rule:BR-BA-001`、`rule:BR-BA-003`、`rule:BR-BA-005`、`rule:BR-BE-001`
|
||||
- **证据**:`documented`
|
||||
|
||||
### action:submit_booking_agent_message
|
||||
|
||||
在 `sessionId + current.userId` 范围内校验草稿版本,将当轮文字解析为非权威约束,再由后端解析真实宠物、服务与号源。
|
||||
|
||||
- **计划入口**:`POST /api/booking-agent/sessions/{sessionId}/messages`
|
||||
- **输入/输出**:`entity:booking_agent_session`、`draftVersion`、`inputType`、`text`
|
||||
- **规则**:`rule:BR-BA-001` … `rule:BR-BA-005`
|
||||
- **触发事件**:首次进入 `confirmable` 时 `event:booking_agent_draft_ready`
|
||||
- **证据**:`documented`
|
||||
|
||||
### action:transcribe_booking_agent_audio
|
||||
|
||||
在本人未终止 session 下将短音频转为可编辑文字,不自动进入意图处理。
|
||||
|
||||
- **计划入口**:`POST /api/booking-agent/sessions/{sessionId}/transcriptions`
|
||||
- **规则**:`rule:BR-BA-001`、`rule:BR-BA-004`、`rule:BR-BA-005`
|
||||
- **证据**:`documented`
|
||||
|
||||
### action:handoff_booking_agent_draft
|
||||
|
||||
返回服务端验证的部分或完整草稿,将会话转为 `fallback`,由现有表单承担最终提交。
|
||||
|
||||
- **计划入口**:`POST /api/booking-agent/sessions/{sessionId}/fallback`
|
||||
- **输出**:一次性草稿交接对象
|
||||
- **规则**:`rule:BR-BA-001`、`rule:BR-BA-003`、`rule:BR-BA-005`
|
||||
- **触发事件**:`event:booking_agent_fallback`
|
||||
- **证据**:`documented`
|
||||
|
||||
### action:cancel_booking_agent_session
|
||||
|
||||
由当前 customer 结束本人未终止会话,仅将 session 标记为 `cancelled`。
|
||||
|
||||
- **计划入口**:`DELETE /api/booking-agent/sessions/{sessionId}`
|
||||
- **规则**:`rule:BR-BA-001`、`rule:BR-BA-003`
|
||||
- **证据**:`documented`
|
||||
|
||||
@ -337,3 +337,35 @@
|
||||
- **触发动作**:`action:view_workbench`
|
||||
- **payload**:`storeId`、`userId`、`ts`
|
||||
- **证据**:`gap`(A 期可不实现上报)
|
||||
|
||||
## 智能预约助手 M0(Batch 0 · documented)
|
||||
|
||||
### event:booking_agent_started
|
||||
|
||||
智能预约 session 创建成功的首次事实。
|
||||
|
||||
- **触发动作**:`action:create_booking_agent_session`
|
||||
- **幂等键**:`booking_agent_started:{sessionDbId}`
|
||||
- **BusinessEvent**:`storeId=session.storeId`、`aggregateType=booking_agent_session`、`aggregateId=session.id`
|
||||
- **metadata 白名单**:`entrySource`
|
||||
- **证据**:`documented`
|
||||
|
||||
### event:booking_agent_draft_ready
|
||||
|
||||
session 首次进入 `confirmable` 的事实,不表示预约已创建。
|
||||
|
||||
- **触发动作**:`action:submit_booking_agent_message`
|
||||
- **幂等键**:`booking_agent_draft_ready:{sessionDbId}`
|
||||
- **metadata 白名单**:`inputModality`
|
||||
- **证据**:`documented`
|
||||
|
||||
### event:booking_agent_fallback
|
||||
|
||||
用户带草稿返回普通表单,或 LLM 失败后进入降级的首次事实。
|
||||
|
||||
- **触发动作**:`action:handoff_booking_agent_draft`
|
||||
- **幂等键**:`booking_agent_fallback:{sessionDbId}`
|
||||
- **metadata 白名单**:`reason=user/llm_unavailable`
|
||||
- **证据**:`documented`
|
||||
|
||||
M0 三个事件均禁止写入输入原文、转写文本、备注、宠物名、服务名、供应商 request ID 或错误原文;M0 不定义 `booking_agent_confirmed`。
|
||||
|
||||
@ -540,3 +540,30 @@ API 动态聚合字段:`pets`、`lastVisitAt`、`lastReportId`、`leadStatus`
|
||||
- `entity:analytics_dashboard`(经营分析看板)— B 期;A 期仅工作台轻量指标
|
||||
|
||||
后续如进入实现,再补完整字段、动作与规则。
|
||||
|
||||
---
|
||||
|
||||
## 智能预约助手 M0(Batch 0 契约)
|
||||
|
||||
## entity:booking_agent_session
|
||||
|
||||
**智能预约短期会话**。它承载已登录 customer 在一个已选门店下的结构化草稿和六态状态机,不是预约,不持有任何预约写入权。
|
||||
|
||||
| 字段 | 说明 |
|
||||
|------|------|
|
||||
| `id` | BIGINT 内部主键;用于低敏 `BusinessEvent.aggregateId` |
|
||||
| `session_id` | 唯一外部 UUID |
|
||||
| `customer_user_id` | 创建会话的 customer,只从登录上下文派生 |
|
||||
| `store_id` | 创建时必填并验证,会话期间固定 |
|
||||
| `status` | `collecting / proposing / confirmable / fallback / expired / cancelled` |
|
||||
| `draft_json` / `draft_version` | 最小已验证草稿与乐观版本 |
|
||||
| `entry_source` | M0 固定 `appointment_create` |
|
||||
| `input_modality` | `text / voice / mixed`,首次成功消息后派生 |
|
||||
| `expires_at` | 创建后 30 分钟,不滑动续期 |
|
||||
| `create_time / update_time` | 服务端审计时间 |
|
||||
|
||||
- **不存储**:原始语音、转写原文、完整对话、提示词、模型原始请求/响应、`appointment_id`。
|
||||
- **归属系统**:Backend Core(计划 `bookingagent` 包)。
|
||||
- **文档**:`docs/架构决策-智能预约助手M0契约与数据模型-2026-08-02.md`。
|
||||
- **证据**:`documented`(尚未创建 JPA 实体,机器图临时 `jpa:false`;实现批必须切换为真实 JPA 证据)。
|
||||
- **约束**:`rule:BR-BA-001` … `rule:BR-BA-005`。
|
||||
|
||||
@ -187,3 +187,18 @@
|
||||
| `business_event` PROJECTS_TO `customer_timeline` | 多条不可变事实经低敏白名单投影为时间线 |
|
||||
| `view_store_customer_timeline` READS `store_customer` / `business_event` | 同店校验后分页读取主档与事实 |
|
||||
| `admin_list_leads` READS `follow_up_task` | `/leads` 导航承载真实操作任务池,不直接编辑留资事实 |
|
||||
|
||||
## 智能预约助手 M0 关系(Batch 0 · documented)
|
||||
|
||||
| 关系 | 说明 |
|
||||
|------|------|
|
||||
| `user` HAS `booking_agent_session` | 一个 customer 可创建多个短期会话,每次读写校验 owner |
|
||||
| `store` HAS `booking_agent_session` | 每个会话在创建时固定一家门店 |
|
||||
| `create_booking_agent_session` CREATES `booking_agent_session` | 创建短期草稿会话 |
|
||||
| `submit_booking_agent_message` UPDATES `booking_agent_session` | 状态与草稿版本乐观更新 |
|
||||
| `transcribe_booking_agent_audio` READS `booking_agent_session` | 用 session 派生 owner/store 上下文,只返回可编辑文本 |
|
||||
| `handoff_booking_agent_draft` UPDATES `booking_agent_session` | 转为 fallback 并返回一次性草稿 |
|
||||
| `cancel_booking_agent_session` UPDATES `booking_agent_session` | 转为 cancelled |
|
||||
| `create_booking_agent_session` EMITS `booking_agent_started` | 创建成功的首次低敏事实 |
|
||||
| `submit_booking_agent_message` EMITS `booking_agent_draft_ready` | 首次进入 confirmable 时记录 |
|
||||
| `handoff_booking_agent_draft` EMITS `booking_agent_fallback` | 用户交接或 LLM 降级的首次事实 |
|
||||
|
||||
@ -479,3 +479,40 @@
|
||||
- **代码**:`backend/src/main/java/com/petstore/service/BusinessEventService.java`、`AdminBusinessEventService.java`
|
||||
- **测试**:`BusinessEventServiceTest`、`BusinessEventMigrationTest`、`AdminBusinessEventControllerTest`
|
||||
- **证据**:`anchored`
|
||||
|
||||
## 智能预约助手 M0(Batch 0 · documented)
|
||||
|
||||
### rule:BR-BA-001
|
||||
|
||||
**customer 与门店数据边界**:所有 endpoint 只允许已登录 customer;session 每次以 `sessionId + current.userId` 查询;他人与不存在 session 同样返回 `SESSION_NOT_FOUND`。`storeId` 只在创建时验证,后续不可覆盖。
|
||||
|
||||
- **适用动作**:M0 全部五个动作
|
||||
- **证据**:`documented`
|
||||
|
||||
### rule:BR-BA-002
|
||||
|
||||
**模型非权威与结构强校验**:模型只返回通过 `booking-intent-v1` 的文本约束,未知字段拒绝。宠物、服务、日期时间和号源由后端确定性重新解析;模型不得产生 ID、价格、号源或预约状态事实。
|
||||
|
||||
- **适用动作**:`action:submit_booking_agent_message`
|
||||
- **证据**:`documented`
|
||||
|
||||
### rule:BR-BA-003
|
||||
|
||||
**状态、版本、TTL 与无写路径**:M0 只允许六个会话状态,草稿更新必须校验 `draftVersion`;TTL 固定 30 分钟且不滑动续期,过期 24 小时后物理清理。M0 不注册 `/confirm`、不调用预约写服务、不修改 `Appointment`。
|
||||
|
||||
- **适用动作**:创建、提交消息、草稿交接、结束会话
|
||||
- **证据**:`documented`
|
||||
|
||||
### rule:BR-BA-004
|
||||
|
||||
**语音、原文与日志最小化**:原始语音、当轮用户原文、完整对话、提示词和模型原始请求/响应不落库、不进事件、不进日志。音频最多 60 秒/3 MB,转写完立即释放,且必须经用户编辑/确认后才发消息。
|
||||
|
||||
- **适用动作**:提交消息、语音转写、草稿交接
|
||||
- **证据**:`documented`
|
||||
|
||||
### rule:BR-BA-005
|
||||
|
||||
**默认关闭、限流与可退化**:`PETSTORE_BOOKING_AGENT_ENABLED` 默认 `false`。每 customer 10 分钟内最多创建 5 会话、30 条消息、10 段语音;模型/ASR/外部查询失败时保留已验证草稿并退回普通表单,不影响普通预约。单实例进程内限流只是 M0 临时边界,多实例前必须改为共享状态。
|
||||
|
||||
- **适用动作**:创建会话、提交消息、语音转写、草稿交接
|
||||
- **证据**:`documented`
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
# 宠小它智能预约助手:产品与技术方案 v0.1
|
||||
|
||||
> 日期:2026-08-02<br>
|
||||
> 状态:**Draft / 待产品、架构、数据与隐私评审;尚未实现**<br>
|
||||
> 状态:**M0 产品/架构/数据契约已冻结;尚未实现;真实供应商与生产隐私评审未完成**<br>
|
||||
> 目标用户:`customer` 宠主<br>
|
||||
> Workstream:`Core Booking Flow`<br>
|
||||
> 建议 Owner:`Product Design` + `Customer Experience FE` + `Backend Core`<br>
|
||||
@ -15,12 +15,16 @@
|
||||
|
||||
- [模型与语音供应商选型](./智能预约助手-模型与语音供应商选型-2026-08-02.md)
|
||||
- [M0 编码任务 brief](./智能预约助手-M0编码任务brief-2026-08-02.md)
|
||||
- [M0 契约与数据模型 ADR](./架构决策-智能预约助手M0契约与数据模型-2026-08-02.md)
|
||||
- [`booking-intent-v1` JSON Schema](./contracts/booking-intent-v1.schema.json)
|
||||
- [M0 OpenAPI 契约](./contracts/booking-agent-m0.openapi.yaml)
|
||||
- [M0 执行队列](./智能预约助手-M0执行队列-2026-08-02.md)
|
||||
|
||||
## 1. 结论
|
||||
|
||||
建议将该能力定义为 **「可执行的智能预约助手」**,而不是泛化客服聊天机器。
|
||||
|
||||
它负责把宠主的自然语言转换为结构化预约草稿,通过现有 Petstore 预约域查询真实宠物、门店服务和可约时段,最后由用户明确确认后才创建 `Appointment`。
|
||||
它负责把宠主的自然语言转换为结构化预约草稿,通过现有 Petstore 预约域查询真实宠物、门店服务和可约时段。M0 只将草稿带入现有表单,仍由用户在普通表单中提交;对话内直接创建 `Appointment` 是 M1 候选能力。
|
||||
|
||||
**核心原则:模型负责「听懂人话」,Petstore 预约域负责「决定什么能约」。**
|
||||
|
||||
@ -38,6 +42,8 @@
|
||||
|
||||
### 2.2 入口优先级
|
||||
|
||||
下列为长期入口价值排序。M0 已冻结为仅从 `CustAppointmentCreate` 顶部次级入口进入,且必须先用现有控件选定门店。
|
||||
|
||||
1. **服务报告或历史预约:「跟助手约下次」**
|
||||
上下文最完整,可在登录与归属校验后预填门店、宠物和上次服务。
|
||||
2. **宠主首页:「一句话预约」**
|
||||
@ -108,6 +114,7 @@
|
||||
|
||||
- 普通预约仍保持「提交时登录 + guest 草稿恢复」的现有口径。
|
||||
- 智能助手 MVP 在创建服务端会话前要求 customer 登录,因为它需要读取归属宠物和历史事实。
|
||||
- M0 在创建会话前还要求已选定有效门店;对话不负责选店,session 创建后不允许切店。
|
||||
- 客户端可在登录前保存入口来源和非敏感意图,但不得在未登录状态调用宠物或历史数据工具。
|
||||
- 会话过期后可将已解析的非敏感草稿回填到普通表单,不能自动续约或提交。
|
||||
|
||||
@ -161,8 +168,10 @@ flowchart LR
|
||||
H --> K[确认卡]
|
||||
I --> K
|
||||
J --> K
|
||||
K --> L[用户明确确认]
|
||||
L --> M[AppointmentService 最终复核与创建]
|
||||
K --> L[M0 带入普通预约]
|
||||
L --> M[CustAppointmentCreate 回填]
|
||||
M --> N[现有预约域复核与创建]
|
||||
K -. M1 另立 ADR 后 .-> O[对话内受控确认]
|
||||
```
|
||||
|
||||
### 6.1 模型可以做的事
|
||||
@ -171,24 +180,24 @@ flowchart LR
|
||||
- 提取非权威文本约束:宠物称呼、服务需求、相对日期、时间偏好、备注;
|
||||
- 根据状态机的下一动作生成简短、可理解的回复。
|
||||
|
||||
建议将模型输出限制为 JSON Schema:
|
||||
模型输出必须通过冻结的 [`booking-intent-v1`](./contracts/booking-intent-v1.schema.json) JSON Schema:
|
||||
|
||||
```json
|
||||
{
|
||||
"intent": "book | modify | end | fallback",
|
||||
"draftPatch": {
|
||||
"petQuery": "球球",
|
||||
"serviceQuery": "洗澡",
|
||||
"dateExpression": "本周六",
|
||||
"timeWindow": { "start": "15:00", "end": null },
|
||||
"remark": null
|
||||
},
|
||||
"schemaVersion": "booking-intent-v1",
|
||||
"intent": "book",
|
||||
"petQuery": "球球",
|
||||
"serviceQuery": "洗澡",
|
||||
"dateExpression": "本周六",
|
||||
"timeWindow": { "start": "15:00", "end": null },
|
||||
"remark": null,
|
||||
"clearFields": [],
|
||||
"ambiguities": [],
|
||||
"nextAction": "ask | resolve_context | search_slots | show_confirmation | fallback"
|
||||
"nextAction": "resolve_context"
|
||||
}
|
||||
```
|
||||
|
||||
`petQuery` 和 `serviceQuery` 只是搜索文本,不是业务 ID。所有 ID 必须由后端在当前登录上下文和门店数据范围中解析。
|
||||
`petQuery` 和 `serviceQuery` 只是搜索文本,不是业务 ID。所有 ID 必须由后端在当前登录上下文和门店数据范围中解析;未知字段和越界枚举直接拒绝,不从残缺输出猜测。
|
||||
|
||||
### 6.2 模型不可以做的事
|
||||
|
||||
@ -209,7 +218,7 @@ flowchart LR
|
||||
| `search_available_slots` | 只读 | 开始/结束时间、可约性 | 换日期或切换表单 |
|
||||
| `resolve_previous_booking` | 只读 | 归属校验后的门店/宠物/服务 ID | 忽略历史预填并继续收集 |
|
||||
|
||||
白名单中不包含写工具。最终创建由确认 endpoint 在模型调用之外完成。
|
||||
白名单中不包含写工具。M0 由普通表单在助手之外完成最终创建;M1 如增加确认 endpoint,也必须在模型调用之外进入预约域。
|
||||
|
||||
## 7. 预约草稿与数据模型提案
|
||||
|
||||
@ -225,43 +234,41 @@ flowchart LR
|
||||
| `timeWindow` | 模型提取 | 否 | 只是偏好约束 |
|
||||
| `appointmentTime` | 真实号源选择 | 是 | 必须是精确的未来时间 |
|
||||
| `remark` | 宠主原意摘要 | 否 | 限长、可编辑,不生成医疗结论 |
|
||||
| `sourceAppointmentId` | 已验证复约入口 | 是 | 可空,必须归属当前 customer |
|
||||
| `draftVersion` | 服务端 | 是 | 每次草稿变更递增,确认时防旧版提交 |
|
||||
|
||||
确认时不直接信任草稿中的展示快照。后端需重新加载 Pet、ServiceType、Store 和容量事实,再由现有预约域生成 `petName`、`petType`、`serviceType`和 `durationMinutes` 快照。
|
||||
|
||||
### 7.2 候选会话表
|
||||
|
||||
长期建议新增 `t_booking_agent_session`,不将对话状态塞入 `t_appointment`。下表是覆盖 M1 直接确认能力的完整候选形态;M0 实际字段以配套编码 brief 为准。
|
||||
新增 `t_booking_agent_session`,不将对话状态塞入 `t_appointment`。M0 字段已由 ADR 冻结:
|
||||
|
||||
| 字段 | 语义 |
|
||||
|---|---|
|
||||
| `session_id` | 不可预测会话 ID,主键 |
|
||||
| `id` | BIGINT 自增内部主键 |
|
||||
| `session_id` | VARCHAR(36) 唯一外部 UUID,不暴露内部主键 |
|
||||
| `customer_user_id` | 从登录上下文派生 |
|
||||
| `store_id` | 会话数据范围 |
|
||||
| `source_appointment_id` | 复约上下文,可空 |
|
||||
| `status` | 仅使用第 5 节对话状态 |
|
||||
| `store_id` | NOT NULL,会话固定数据范围 |
|
||||
| `status` | M0 六个状态之一 |
|
||||
| `draft_json` | 最小结构化草稿,不存原始语音 |
|
||||
| `draft_version` | 乐观版本号 |
|
||||
| `entry_source` | `appointment_create` / `home` / `appointment_history` / `report_history` / `pet_profile` |
|
||||
| `entry_source` | M0 固定 `appointment_create` |
|
||||
| `input_modality` | `text` / `voice` / `mixed` |
|
||||
| `appointment_id` | M1 成功后关联的真实预约,可空;M0 不增加 |
|
||||
| `expires_at` | 会话过期时间 |
|
||||
| `expires_at` | 创建后 30 分钟,不滑动续期 |
|
||||
| `create_time` / `update_time` | 审计时间 |
|
||||
|
||||
数据建议:
|
||||
|
||||
- 以 `(customer_user_id, status, expires_at)` 建立查询索引;
|
||||
- M1 增加 `appointment_id` 可空唯一关联;
|
||||
- M1 确认时对 session 行加悲观锁;如已是 `booked`,重复请求直接返回同一 `Appointment`;
|
||||
- MVP 会话默认 30 分钟过期,过期不等于删除预约;
|
||||
- 以 `(status, expires_at)` 支持过期标记和清理;
|
||||
- 会话过期 24 小时后物理删除,M0 不增加 `deleted`;
|
||||
- M0 不增加 `source_appointment_id` 或 `appointment_id`;
|
||||
- 不新增 `t_booking_agent_turn` 原文表;模型每轮以当前草稿和当次输入工作,避免默认持久化完整对话。
|
||||
|
||||
该表、索引、过期清理和迁移顺序均是 **待 Data Model 评审的提案**,不代表当前生产库已存在。
|
||||
上述是 **M0 已评审契约**,不代表当前生产库已存在;迁移实现 diff 仍必须经 Data Model Review。
|
||||
|
||||
### 7.3 预约来源与事件
|
||||
|
||||
不改变现有 `BusinessEvent.source=customer/admin`的操作人语义。建议在 `appointment_created` 的 `metadata_json` 增加独立维度:
|
||||
不改变现有 `BusinessEvent.source=customer/admin`的操作人语义。长期可在 `appointment_created` 的 `metadata_json` 评审独立维度:
|
||||
|
||||
```json
|
||||
{
|
||||
@ -273,119 +280,52 @@ flowchart LR
|
||||
|
||||
`bookingChannel` 候选值:`form`、`agent_draft`、`agent_text`、`agent_voice`、`admin`、`follow_up`。`agent_draft` 只表示智能助手提供了已验证草稿;`agent_text` 和 `agent_voice` 保留给 M1 对话内直接创建路径。初始 M0 不修改 `appointment_created` 事件,如需归因表单提交,必须另行评审服务端验证的一次性草稿交接,不接受客户端自由上报渠道。不建议 MVP 为此修改 `Appointment` 主表。
|
||||
|
||||
候选新事件:
|
||||
冻结的 M0 新事件:
|
||||
|
||||
- `booking_agent_started`;
|
||||
- `booking_agent_draft_ready`;
|
||||
- `booking_agent_fallback`;
|
||||
- `booking_agent_confirmed`(M1)。
|
||||
- `booking_agent_fallback`。
|
||||
|
||||
这些事件只用于漏斗和运行分析;只有真实创建成功的 `appointment_created` 才计为预约。编码前需将候选对象、动作、事件和规则同步到 `docs/ontology/` 及 `graph/ontology.jsonl`。
|
||||
这些事件只用于漏斗和运行分析;只有真实创建成功的 `appointment_created` 才计为预约。M0 不增加 `booking_agent_confirmed`,不修改现有 `appointment_created`。
|
||||
|
||||
## 8. API 契约草案
|
||||
## 8. M0 API 冻结契约
|
||||
|
||||
本节是评审草案,尚未实现。响应沿用当前 `{ code, message, bizCode, data }` 业务外壳,所有 endpoint 都要求 customer session token。
|
||||
契约已冻结、尚未实现。唯一机读权威是 [M0 OpenAPI](./contracts/booking-agent-m0.openapi.yaml);响应沿用当前 `{ code, message, bizCode, data }` 外壳,除鉴权拦截的 HTTP 401 外,客户端以 body `code` 为业务结果。本功能不顺带重构全局响应语义。
|
||||
|
||||
### 8.1 创建会话
|
||||
### 8.1 Endpoint
|
||||
|
||||
`POST /api/booking-agent/sessions`
|
||||
|
||||
```json
|
||||
{
|
||||
"entrySource": "home",
|
||||
"storeId": 1,
|
||||
"sourceAppointmentId": null
|
||||
}
|
||||
```
|
||||
|
||||
规则:
|
||||
|
||||
- 不接受 `customerUserId`,必须从 `CurrentUserContext` 派生;
|
||||
- `sourceAppointmentId` 非空时必须归属当前 customer;
|
||||
- `storeId` 与源预约冲突时拒绝,不静默覆盖;
|
||||
- 返回 `sessionId`、`status`、`draft`、`assistantMessage`、`expiresAt` 和 `draftVersion`。
|
||||
|
||||
### 8.2 提交文字或语音转写结果
|
||||
|
||||
`POST /api/booking-agent/sessions/{sessionId}/messages`
|
||||
|
||||
```json
|
||||
{
|
||||
"inputType": "text",
|
||||
"text": "周六下午给球球洗澡,三点以后都行"
|
||||
}
|
||||
```
|
||||
|
||||
建议响应 `data`:
|
||||
|
||||
```json
|
||||
{
|
||||
"sessionId": "<opaque-session-id>",
|
||||
"status": "proposing",
|
||||
"assistantMessage": "本周六有 3 个符合的时段",
|
||||
"draft": {},
|
||||
"slotOptions": [
|
||||
{ "startTime": "2026-08-08T15:00:00", "endTime": "2026-08-08T16:00:00" }
|
||||
],
|
||||
"quickReplies": ["15:00", "16:30", "17:00", "换一天"],
|
||||
"confirmable": false,
|
||||
"draftVersion": 2
|
||||
}
|
||||
```
|
||||
|
||||
客户端只展示可理解错误,不展示模型原始输出、提示词、堆栈或供应商错误。
|
||||
|
||||
### 8.3 语音转写
|
||||
|
||||
`POST /api/booking-agent/transcriptions`
|
||||
|
||||
- `multipart/form-data`,仅接受允许的音频 MIME 和扩展名;
|
||||
- M0 单段不超过 60 秒、3 MB,与供应商选型和编码 brief 保持一致;
|
||||
- 后端代理调用语音服务,不向客户端下发供应商密钥;
|
||||
- 语音只用于当次转写,不进入通用报告媒体存储,不持久化原始录音;
|
||||
- 返回可编辑 `text` 和脱敏 `requestId`,宠主可修改识别结果再发送。
|
||||
|
||||
### 8.4 确认预约(M1,M0 不实现)
|
||||
|
||||
`POST /api/booking-agent/sessions/{sessionId}/confirm`
|
||||
|
||||
```json
|
||||
{
|
||||
"draftVersion": 3
|
||||
}
|
||||
```
|
||||
|
||||
确认 endpoint 必须:
|
||||
|
||||
1. 从 session token 重新派生 customer;
|
||||
2. 对 `t_booking_agent_session` 行加锁;
|
||||
3. 校验 `draftVersion`、会话状态和过期时间;
|
||||
4. 重新校验宠物归属、门店服务和号源;
|
||||
5. 调用现有 `AppointmentService.createBooking`,保留门店行锁和连续容量桶判定;
|
||||
6. 记录 `appointment_created` 及助手漏斗事件;
|
||||
7. 将 session 更新为 `booked` 并关联 `appointmentId`。
|
||||
|
||||
并发或重试时,同一 session 只能创建一个预约;后续重复确认返回已创建的同一预约。
|
||||
|
||||
### 8.5 返回草稿到普通表单
|
||||
|
||||
`POST /api/booking-agent/sessions/{sessionId}/fallback`
|
||||
|
||||
返回经后端验证的可回填字段,客户端导航到现有 `CustAppointmentCreate`。不返回供应商上下文、提示词、置信度或内部调试字段。
|
||||
|
||||
### 8.6 业务码草案
|
||||
|
||||
| `bizCode` | 用户语义 | 前端处理 |
|
||||
| Method | Endpoint | M0 语义 |
|
||||
|---|---|---|
|
||||
| `AGENT_SESSION_NOT_FOUND` | 会话不存在或不属于当前宠主 | 返回预约入口 |
|
||||
| `AGENT_SESSION_EXPIRED` | 会话已过期 | 新建会话或切表单 |
|
||||
| `AGENT_INPUT_INVALID` | 输入为空或超限 | 允许重新输入 |
|
||||
| `AGENT_UNAVAILABLE` | 模型超时或输出校验失败 | 保留草稿并切表单 |
|
||||
| `ASR_UNAVAILABLE` | 语音转写失败 | 重试或改用文字 |
|
||||
| `DRAFT_VERSION_CONFLICT` | 确认的不是最新草稿 | 刷新确认卡 |
|
||||
| `PET_CUSTOMER_MISMATCH` | 宠物归属校验失败 | 重新选宠物 |
|
||||
| `SERVICE_TYPE_INVALID` | 服务不属于门店或已下线 | 重新选服务 |
|
||||
| `CAPACITY_FULL` | 确认时号源已变化 | 立即查询新候选时段 |
|
||||
| `POST` | `/api/booking-agent/sessions` | 以已选 `storeId` 创建 customer 会话;`entrySource` 由服务端固定 |
|
||||
| `POST` | `/api/booking-agent/sessions/{sessionId}/messages` | 携带 `draftVersion`、`inputType` 和文本更新草稿 |
|
||||
| `POST` | `/api/booking-agent/sessions/{sessionId}/transcriptions` | 在本人 session 下转写不超过 60 秒/3 MB 的短音频,仅返回可编辑文本 |
|
||||
| `POST` | `/api/booking-agent/sessions/{sessionId}/fallback` | 携带 `draftVersion`,返回服务端验证的一次性回填草稿 |
|
||||
| `DELETE` | `/api/booking-agent/sessions/{sessionId}` | 将本人非终态会话标记为 `cancelled` |
|
||||
|
||||
所有 endpoint 只允许已登录 `customer`;session 每次用 `sessionId + current.userId` 查询,他人 session 和不存在 session 统一为 `SESSION_NOT_FOUND`。创建后 `storeId` 固定,消息和 fallback 必须进行草稿版本检查。
|
||||
|
||||
M0 不注册 `/confirm`,不调用 `AppointmentService.createBooking`;M1 必须新立 ADR 审查写路径、行锁、幂等和事件归因。
|
||||
|
||||
### 8.2 业务码
|
||||
|
||||
| body code | `bizCode` | 语义 |
|
||||
|---|---|---|
|
||||
| 401 | `UNAUTHENTICATED` | session token 缺失或失效 |
|
||||
| 403 | `FORBIDDEN` | 非 customer 调用 |
|
||||
| 503 | `AGENT_DISABLED` | 功能开关关闭 |
|
||||
| 404 | `STORE_NOT_FOUND` | 创建时目标门店无效 |
|
||||
| 404 | `SESSION_NOT_FOUND` | session 不存在或不属于当前 customer |
|
||||
| 410 | `SESSION_EXPIRED` | 会话 TTL 到期 |
|
||||
| 409 | `SESSION_TERMINAL` | 会话已 fallback/cancelled |
|
||||
| 409 | `DRAFT_VERSION_CONFLICT` | 客户端草稿版本过旧 |
|
||||
| 400 | `INVALID_INPUT` | 空文本、超长或请求结构无效 |
|
||||
| 400 | `INVALID_AUDIO` | 空音频、格式/MIME/文件头不匹配或时长非法 |
|
||||
| 413 | `AUDIO_TOO_LARGE` | 超过 3 MB |
|
||||
| 429 | `RATE_LIMITED` | customer 请求频率超限 |
|
||||
| 503 | `AGENT_UNAVAILABLE` | LLM 超时、无效 JSON/schema 或外部失败 |
|
||||
| 503 | `ASR_UNAVAILABLE` | ASR 超时或外部失败 |
|
||||
|
||||
宠物/服务歧义、无号源和未选时段是正常 200 会话响应,不用异常码表达。
|
||||
|
||||
## 9. 语音与模型集成边界
|
||||
|
||||
@ -409,7 +349,7 @@ flowchart LR
|
||||
- 语音失败:保留本地输入状态,提供「重新说」和「改用文字」。
|
||||
- 模型失败:不自动重复多次调用;保留已验证草稿并导向普通表单。
|
||||
- 工具查询失败:展示业务错误,不让模型根据记忆补齐结果。
|
||||
- 最终号源冲突:会话进入 `needs_reselection`,不创建失败预约或假成功记录。
|
||||
- M1 最终号源冲突:会话进入 `needs_reselection`,不创建失败预约或假成功记录。M0 只查询候选号源并回填表单。
|
||||
|
||||
## 10. 安全、隐私与可观测性
|
||||
|
||||
@ -476,7 +416,7 @@ flowchart LR
|
||||
|
||||
### M1:受控确认创建
|
||||
|
||||
- 增加 session 表、确认 endpoint、行锁和幂等返回;
|
||||
- 在 M0 session 表上增加 `appointment_id`、确认 endpoint、行锁和幂等返回;
|
||||
- 确认时重新校验权限、宠物归属、服务和容量;
|
||||
- 补齐助手漏斗事件和 `bookingChannel`;
|
||||
- 通过硬性安全验收后,再允许确认卡直接创建预约。
|
||||
@ -492,19 +432,21 @@ flowchart LR
|
||||
|
||||
### 13.1 硬性门禁
|
||||
|
||||
- [ ] 未点击「确认预约」前不创建 `Appointment`。
|
||||
- [ ] M0 不存在助手 `/confirm` endpoint,不调用预约写服务。
|
||||
- [ ] 助手不会返回门店不存在的服务或后端未返回的号源。
|
||||
- [ ] 确认时复核服务时长、连续容量桶和门店锁,不产生超卖。
|
||||
- [ ] 同一 session 并发或重复确认只有一个 `Appointment`。
|
||||
- [ ] 草稿只使用当前 customer 的宠物、session 门店服务和后端真实号源。
|
||||
- [ ] 门店在会话前选定,session 创建后不能跨店更改。
|
||||
- [ ] A customer 不能查询或选择 B customer 的宠物、历史预约或 session。
|
||||
- [ ] 不将手机号、完整 `report_token`、session token 或密钥发送给模型或写入日志。
|
||||
- [ ] 原始语音转写后立即释放,不出现在通用上传目录或报告媒体中。
|
||||
- [ ] 模型、语音或工具失败时能保留已验证草稿并切换普通表单。
|
||||
- [ ] `Appointment.status` 仍只有 `new`、`doing`、`done`、`cancel`。
|
||||
- [ ] 助手生成的真实预约有 `appointment_created` 事件和可统计 `bookingChannel`。
|
||||
- [ ] 仅记录 M0 三个漏斗事件,不修改 `appointment_created` 事件语义。
|
||||
|
||||
### 13.2 价值验证建议
|
||||
|
||||
首批 M0 先观察 `draft_ready / started`、`fallback / started`、语音转写修改率和普通表单回填成功率。下列「助手到真实预约」指标只能在有可验证的服务端草稿归因后使用,不接受客户端自由上报。
|
||||
|
||||
首批 20 个真实 session 用于发现语料和交互问题,不立即下商业化结论。稳定后可对 20~50 个老客 session 使用以下建议门槛:
|
||||
|
||||
- 助手启动到真实预约成功率 ≥ 70%;
|
||||
@ -515,22 +457,27 @@ flowchart LR
|
||||
|
||||
上述价值门槛是试点假设,应在首批真实数据后与普通表单基线一起复核;不得用演示会话、测试预约或未履约预约代替真实样本。
|
||||
|
||||
## 14. 开发前待冻结决策
|
||||
## 14. M0 已冻结决策与生产前缺口
|
||||
|
||||
| ID | 决策 | 建议默认 | 责任人 |
|
||||
|---|---|---|---|
|
||||
| D1 | M0 首发入口 | `CustAppointmentCreate` 顶部次级入口;历史预约、报告和首页入口后续评估 | Product Design |
|
||||
| D1 | M0 首发入口 | `CustAppointmentCreate` 顶部次级入口;先选门店,再登录/创建 session | Product Design |
|
||||
| D2 | 是否支持匿名助手 | MVP 不支持,普通表单继续支持 guest 草稿 | Product Design + System Architect |
|
||||
| D3 | M0 是否直接创建预约 | 否,先回填现有表单 | Product Design + Backend Core |
|
||||
| D4 | 模型与语音供应商 | M0 推荐阿里云百炼北京 `qwen3-asr-flash-2026-02-10 + qwen-plus-2025-12-01`,腾讯 ASR 作对照;生产前仍需语料和隐私复核 | System Architect + Backend Ops |
|
||||
| D4 | 模型与语音供应商 | 开发适配目标已选;生产开启仍受语料对照、数据区域和隐私复核阻塞 | System Architect + Backend Ops |
|
||||
| D5 | 原文保留 | 默认不持久化对话原文,不持久化原始语音 | Access / Privacy Review |
|
||||
| D6 | session TTL | MVP 30 分钟,编码前由 Data Model 冻结 | Data Model |
|
||||
| D6 | session TTL | 创建后固定 30 分钟,不滑动续期;过期 24 小时后物理删除 | Data Model |
|
||||
| D7 | 助手对话风格 | 简短、一次一个关键问题,不模拟人格客服 | Product Design |
|
||||
| D8 | 试点启动时机 | RC6 基础链路已用真实门店跑通并建立普通表单基线后 | 主控 PM |
|
||||
|
||||
## 15. 实施任务队列草案
|
||||
## 15. 实施任务队列
|
||||
|
||||
以下任务默认为 `Backlog`,不因本文档创建而自动进入开发。
|
||||
当前可执行状态、owner、锁、依赖和验收口径只以 [M0 执行队列](./智能预约助手-M0执行队列-2026-08-02.md) 为准。其中 Batch 1–3 已具备开发边界;真实供应商和生产开启仍为 `Blocked`。
|
||||
|
||||
<details>
|
||||
<summary>已被独立执行队列取代的历史草案(仅供审计,不执行)</summary>
|
||||
|
||||
以下 BA-01~BA-07 是契约冻结前的原始 Backlog,不再表示当前任务状态。
|
||||
|
||||
### BA-01 冻结智能预约产品与交互契约
|
||||
|
||||
@ -707,12 +654,14 @@ Data Model Review Required: Yes
|
||||
Access / Privacy Review Required: Yes
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
## 16. 开发与验证清单
|
||||
|
||||
实现时至少需要:
|
||||
|
||||
1. 更新 `docs/ontology/objects.md`、`actions.md`、`events.md`、`rules.md`、`relations.md` 和 `graph/ontology.jsonl`;
|
||||
2. 后端覆盖模型无效输出、跨 customer/跨店、宠物归属、服务下线、号源冲突、重复确认和 session 过期;
|
||||
2. 后端覆盖模型无效输出、跨 customer/跨店、宠物归属、服务下线、无号源、`draftVersion` 冲突、session 过期和「不存在助手写路径」;
|
||||
3. 前端覆盖语音失败、模型失败、空输入、草稿修改、无号源、提交 loading 和普通表单降级;
|
||||
4. 生产前检查供应商密钥、允许数据区域、超时、频控、日志脱敏和降级开关;
|
||||
5. 以普通预约为基线,不用智能助手取代现有真实门店试点指标。
|
||||
@ -737,6 +686,6 @@ git -C /Users/apple/_src/petstore/docs diff --check
|
||||
| 门店服务 | 服务项目具有名称和 `durationMinutes` | 智能助手只从真实列表中匹配 |
|
||||
| 可约时段 | `/api/appointment/available-slots` 按时长和容量返回 | 可复用领域服务,不由模型计算 |
|
||||
| 预约创建 | `AppointmentService.createBooking` 复核宠物、服务、时间和容量 | M1 确认 endpoint 的唯一写入权威 |
|
||||
| 业务事件 | `appointment_created` 已持久化 | 增加独立 `bookingChannel` 和助手漏斗事件 |
|
||||
| 业务事件 | `appointment_created` 已持久化 | M0 只增加三个助手漏斗事件;`bookingChannel` 与直接创建归因留待 M1 评审 |
|
||||
|
||||
本方案是对现有预约入口的语义化扩展,不建立第二套预约、容量或身份系统。
|
||||
|
||||
181
智能预约助手-M0执行队列-2026-08-02.md
Normal file
181
智能预约助手-M0执行队列-2026-08-02.md
Normal file
@ -0,0 +1,181 @@
|
||||
# 智能预约助手 M0 执行队列
|
||||
|
||||
> 日期:2026-08-02<br>
|
||||
> 状态:**Batch 0 contract frozen / coding not started**<br>
|
||||
> RC Included:`No`<br>
|
||||
> 当前优先级说明:不抢占 `phase2-pilot-rc6` 真实门店基线问题;按独立 feature flag 开发
|
||||
|
||||
**冻结输入**
|
||||
|
||||
- [架构决策:M0 契约与数据模型](./架构决策-智能预约助手M0契约与数据模型-2026-08-02.md)
|
||||
- [`booking-intent-v1` JSON Schema](./contracts/booking-intent-v1.schema.json)
|
||||
- [M0 OpenAPI 契约](./contracts/booking-agent-m0.openapi.yaml)
|
||||
- [M0 编码任务 brief](./智能预约助手-M0编码任务brief-2026-08-02.md)
|
||||
|
||||
## 队列摘要
|
||||
|
||||
| Task Name | Owner Agent | Status | Depends On |
|
||||
|---|---|---|---|
|
||||
| 实现智能预约供应商适配与契约测试 | Backend Core | Ready | Batch 0 冻结资产 |
|
||||
| 实现智能预约会话与只读预约编排 | Backend Core | Ready | Batch 0 冻结资产;provider interface |
|
||||
| 实现宠主一句话预约与草稿回填 | Customer Experience FE | Ready | OpenAPI;可先用 mock |
|
||||
| 验收智能预约 M0 草稿链路 | Core Flow QA | Ready | 可先写 fixture/smoke;执行依赖前三项 |
|
||||
| 完成真实供应商对照与生产隐私复核 | Backend Ops | Blocked | 云账号、密钥、授权语音和上线授权 |
|
||||
|
||||
## Task:实现智能预约供应商适配与契约测试
|
||||
|
||||
| 字段 | 值 |
|
||||
|---|---|
|
||||
| Task Name | 实现智能预约供应商适配与契约测试 |
|
||||
| Role | `Backend Core` |
|
||||
| Owner Agent | `Backend Core` |
|
||||
| Paired QA | `Core Flow QA` |
|
||||
| Workstream | `Core Booking Flow` |
|
||||
| Track | `Coding` |
|
||||
| Status | `Ready` |
|
||||
| Priority | `P0` |
|
||||
| Repo/Path | `backend/src/main/java/com/petstore/bookingagent/provider`、对应 config/test |
|
||||
| Service Lock | `backend:booking-agent-provider` |
|
||||
| Depends On | M0 ADR、JSON Schema、供应商选型 |
|
||||
| Acceptance | provider interface + 本地 HTTP stub;30 条文字 fixture;无效 JSON/schema 降级;未配置密钥时应用正常启动 |
|
||||
| RC Included | `No` |
|
||||
| Due Date | `TBD` |
|
||||
| Output Link | `TBD — implementation commit` |
|
||||
| Blocker Owner | `None`(真实 provider smoke 不在本任务验收内) |
|
||||
| Commit / RC | `Not Frozen` |
|
||||
| Architecture Review Required | `Yes — completed by M0 ADR` |
|
||||
| Data Model Review Required | `No` |
|
||||
| Access / Privacy Review Required | `Yes — development boundary completed; live smoke pending` |
|
||||
|
||||
额外门禁:
|
||||
|
||||
- 使用 JDK `HttpClient` 和可注入 timeout;
|
||||
- 测试不得访问公网;
|
||||
- prompt、原文、Authorization、API Key 和模型原始响应不进日志;
|
||||
- `booking-intent-v1` 未通过完整校验时不返回部分 patch。
|
||||
|
||||
## Task:实现智能预约会话与只读预约编排
|
||||
|
||||
| 字段 | 值 |
|
||||
|---|---|
|
||||
| Task Name | 实现智能预约会话与只读预约编排 |
|
||||
| Role | `Backend Core` |
|
||||
| Owner Agent | `Backend Core` |
|
||||
| Paired QA | `Core Flow QA` |
|
||||
| Workstream | `Core Booking Flow` |
|
||||
| Track | `Coding` |
|
||||
| Status | `Ready` |
|
||||
| Priority | `P0` |
|
||||
| Repo/Path | `backend/src/main/java/com/petstore/bookingagent`、`backend/db/migrations/20260802_create_booking_agent_session.sql` |
|
||||
| Service Lock | `backend:booking-agent-session+appointment-read-tools` |
|
||||
| Depends On | M0 ADR、OpenAPI、provider interface |
|
||||
| Acceptance | 五个 endpoint;customer 归属;六态状态机;draftVersion;30 分钟 TTL/24 小时清理;只读宠物/服务/容量;三个低敏事件;不存在 `/confirm` |
|
||||
| RC Included | `No` |
|
||||
| Due Date | `TBD` |
|
||||
| Output Link | `TBD — implementation commit` |
|
||||
| Blocker Owner | `None` |
|
||||
| Commit / RC | `Not Frozen` |
|
||||
| Architecture Review Required | `Yes — completed by M0 ADR` |
|
||||
| Data Model Review Required | `Yes — proposal completed; implementation diff requires Data Model review` |
|
||||
| Access / Privacy Review Required | `Yes — development boundary completed` |
|
||||
|
||||
额外门禁:
|
||||
|
||||
- `storeId` 创建时必填,后续只从 session 读取;
|
||||
- session 必须用 `sessionId + current.userId` 查询;
|
||||
- 整套测试验证不调用 `AppointmentService.createBooking` / `AppointmentMapper.save`;
|
||||
- 迁移包含索引、状态验证和 TTL 清理验证查询;
|
||||
- 实现时同步把对应本体证据从 `documented` 更新为 `anchored`。
|
||||
|
||||
## Task:实现宠主一句话预约与草稿回填
|
||||
|
||||
| 字段 | 值 |
|
||||
|---|---|
|
||||
| Task Name | 实现宠主一句话预约与草稿回填 |
|
||||
| Role | `Customer Experience Frontend` |
|
||||
| Owner Agent | `Customer Experience FE` |
|
||||
| Paired QA | `Core Flow QA` |
|
||||
| Workstream | `Core Booking Flow` |
|
||||
| Track | `Coding` |
|
||||
| Status | `Ready` |
|
||||
| Priority | `P0` |
|
||||
| Repo/Path | `frontend/src/pages/appointment`、`frontend/src/api/index.js`、`frontend/src/utils/bookingAgentDraft.js`、`frontend/src/pages.json` |
|
||||
| Service Lock | `frontend:customer-booking-agent+shared-api` |
|
||||
| Depends On | M0 OpenAPI;后端未完成前允许契约 mock |
|
||||
| Acceptance | 先选门店;customer 入口;文字链路;按住说话与隐私告知;转写可编辑;选择芯片;草稿卡;一次性回填;失败不影响普通表单 |
|
||||
| RC Included | `No` |
|
||||
| Due Date | `TBD` |
|
||||
| Output Link | `TBD — implementation commit` |
|
||||
| Blocker Owner | `None`(真机录音最终验收依赖测试设备) |
|
||||
| Commit / RC | `Not Frozen` |
|
||||
| Architecture Review Required | `Yes — completed by M0 ADR` |
|
||||
| Data Model Review Required | `No` |
|
||||
| Access / Privacy Review Required | `Yes — 语音首次告知与权限拒绝降级必须 review` |
|
||||
|
||||
额外门禁:
|
||||
|
||||
- 未选门店时先使用现有门店控件,不创建 agent session;
|
||||
- 转写不得自动调用 messages;
|
||||
- 对话消息不写 Storage,一次性 handoff 草稿消费后删除;
|
||||
- `frontend/src/api/index.js` 写入前通知 Backend Core 并持有 `shared-api` 锁;
|
||||
- H5 不具备录音能力时只隐藏语音按钮,文字主链路仍可用。
|
||||
|
||||
## Task:验收智能预约 M0 草稿链路
|
||||
|
||||
| 字段 | 值 |
|
||||
|---|---|
|
||||
| Task Name | 验收智能预约 M0 草稿链路 |
|
||||
| Role | `Core Flow QA` |
|
||||
| Owner Agent | `Core Flow QA` |
|
||||
| Paired QA | `Backend Core` + `Customer Experience FE` |
|
||||
| Workstream | `Core Booking Flow` |
|
||||
| Track | `QA` |
|
||||
| Status | `Ready` |
|
||||
| Priority | `P0` |
|
||||
| Repo/Path | `docs/qa-reports`、必要 smoke 脚本;不改业务代码 |
|
||||
| Service Lock | `docs:qa-booking-agent-m0` |
|
||||
| Depends On | 立即可写 fixture/smoke;执行依赖三个实现任务 |
|
||||
| Acceptance | 权限/跨 customer;状态与版本;时间解析;真实号源;语音限制;日志脱敏;无写路径;普通预约回归;H5/mp-weixin 构建 |
|
||||
| RC Included | `No` |
|
||||
| Due Date | `TBD` |
|
||||
| Output Link | `TBD — QA report` |
|
||||
| Blocker Owner | `None` |
|
||||
| Commit / RC | `Not Frozen` |
|
||||
| Architecture Review Required | `No` |
|
||||
| Data Model Review Required | `No` |
|
||||
| Access / Privacy Review Required | `Yes — 验收日志与语音告知` |
|
||||
|
||||
QA 必须提供反证:
|
||||
|
||||
- 后端没有 `/api/booking-agent/sessions/{sessionId}/confirm`;
|
||||
- M0 测试执行期间没有 `AppointmentMapper.save`;
|
||||
- 他人/不存在 session 返回同一 `SESSION_NOT_FOUND`;
|
||||
- provider 超时、无效 JSON、ASR 失败均能回到普通预约;
|
||||
- 原始语音、当轮原文、模型响应和密钥不出现在库、日志、截图或 fixture。
|
||||
|
||||
## Task:完成真实供应商对照与生产隐私复核
|
||||
|
||||
| 字段 | 值 |
|
||||
|---|---|
|
||||
| Task Name | 完成真实供应商对照与生产隐私复核 |
|
||||
| Role | `Backend Ops` |
|
||||
| Owner Agent | `Backend Ops` |
|
||||
| Paired QA | `Core Flow QA` |
|
||||
| Workstream | `Release` |
|
||||
| Track | `Review` |
|
||||
| Status | `Blocked` |
|
||||
| Priority | `P1` |
|
||||
| Repo/Path | 本地密钥存储、低敏 smoke 证据、`docs/qa-reports`;不改业务逻辑 |
|
||||
| Service Lock | `ops:booking-agent-provider-smoke` |
|
||||
| Depends On | 阿里云北京 Workspace/API Key;20 条已授权语音;隐私告知;实现任务完成 |
|
||||
| Acceptance | 30 条文字/20 条语音门槛;腾讯 ASR 对照;p50/p95;费用告警;日志审计;默认 flag 关闭 |
|
||||
| RC Included | `No` |
|
||||
| Due Date | `TBD` |
|
||||
| Output Link | `TBD — provider evaluation report` |
|
||||
| Blocker Owner | `Product Owner / Access & Privacy / Cloud Account Owner` |
|
||||
| Commit / RC | `Not Frozen` |
|
||||
| Architecture Review Required | `Yes — supplier result may change baseline` |
|
||||
| Data Model Review Required | `No` |
|
||||
| Access / Privacy Review Required | `Yes — pending` |
|
||||
|
||||
阻塞条件不会阻塞前三项本地开发。真实 Workspace ID 和 API Key 只从密钥存储读取,不进入任务评论或 Git。
|
||||
@ -1,32 +1,38 @@
|
||||
# 智能预约助手 M0 编码任务 brief
|
||||
|
||||
> 日期:2026-08-02<br>
|
||||
> 状态:**Ready for Review / 尚未进入编码**<br>
|
||||
> 状态:**Ready / Batch 0 契约已冻结,尚未进入编码**<br>
|
||||
> 目标:宠主用文字或语音生成可验证预约草稿,回填现有表单,不由智能层创建 `Appointment`<br>
|
||||
> Workstream:`Core Booking Flow`<br>
|
||||
> Owner Agent:`Backend Core` + `Customer Experience FE`<br>
|
||||
> Paired QA:`Core Flow QA`<br>
|
||||
> Service Lock:`backend:booking-agent-session+read-tools` + `frontend:customer-booking-agent+shared-api`<br>
|
||||
> Data Model Review Required:`Yes`<br>
|
||||
> Access / Privacy Review Required:`Yes`<br>
|
||||
> Architecture Review Required:`Yes — completed by M0 ADR`<br>
|
||||
> Data Model Review Required:`Yes — proposal completed; implementation diff requires review`<br>
|
||||
> Access / Privacy Review Required:`Yes — development boundary completed; live provider/production pending`<br>
|
||||
> RC Included:`No`
|
||||
|
||||
**需求来源**
|
||||
|
||||
- [智能预约助手产品与技术方案 v0.1](./宠小它智能预约助手-产品与技术方案-v0.1.md)
|
||||
- [模型与语音供应商选型](./智能预约助手-模型与语音供应商选型-2026-08-02.md)
|
||||
- [M0 契约与数据模型 ADR](./架构决策-智能预约助手M0契约与数据模型-2026-08-02.md)
|
||||
- [`booking-intent-v1` JSON Schema](./contracts/booking-intent-v1.schema.json)
|
||||
- [M0 OpenAPI 契约](./contracts/booking-agent-m0.openapi.yaml)
|
||||
- [M0 执行队列](./智能预约助手-M0执行队列-2026-08-02.md)
|
||||
|
||||
## 1. M0 交付结果
|
||||
|
||||
宠主在已登录状态进入「一句话预约」,输入文字或按住说话。系统依次:
|
||||
宠主在普通预约页先选定门店,并在已登录状态进入「一句话预约」。系统依次:
|
||||
|
||||
1. 转写语音(文字输入跳过);
|
||||
2. 提取宠物查询词、服务查询词、日期表达、时间偏好和备注;
|
||||
3. 在当前 customer 和门店范围内解析真实 Pet 与 ServiceType;
|
||||
4. 调用现有容量服务获取真实号源;
|
||||
5. 展示可修改草稿卡;
|
||||
6. 宠主点击「带入普通预约」后,回填现有 `CustAppointmentCreate`;
|
||||
7. 宠主仍在现有表单上点击「提交预约」。
|
||||
1. 使用已选 `storeId` 创建会话;
|
||||
2. 转写语音(文字输入跳过);
|
||||
3. 提取宠物查询词、服务查询词、日期表达、时间偏好和备注;
|
||||
4. 在当前 customer 和 session 门店范围内解析真实 Pet 与 ServiceType;
|
||||
5. 调用现有容量服务获取真实号源;
|
||||
6. 展示可修改草稿卡;
|
||||
7. 宠主点击「带入普通预约」后,回填现有 `CustAppointmentCreate`;
|
||||
8. 宠主仍在现有表单上点击「提交预约」。
|
||||
|
||||
M0 成功的判定是 **「草稿真实、回填正确、普通预约未受影响」**,不是对话内创建预约。
|
||||
|
||||
@ -47,21 +53,24 @@ M0 成功的判定是 **「草稿真实、回填正确、普通预约未受影
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A[宠主预约页] --> B[点击一句话预约]
|
||||
B --> C{已登录 customer}
|
||||
C -- 否 --> D[登录并保留返回路由]
|
||||
D --> E[创建助手会话]
|
||||
B --> C{已选有效门店}
|
||||
C -- 否 --> D[使用现有控件选择门店]
|
||||
D --> E{已登录 customer}
|
||||
C -- 是 --> E
|
||||
E --> F[文字或语音输入]
|
||||
F --> G[结构化意图提取]
|
||||
G --> H[服务端解析真实宠物和服务]
|
||||
H --> I[查询真实号源]
|
||||
I --> J{草稿完整}
|
||||
J -- 否 --> K[询问一个关键缺失项]
|
||||
K --> F
|
||||
J -- 是 --> L[展示草稿卡]
|
||||
L --> M[带入普通预约]
|
||||
M --> N[CustAppointmentCreate 回填]
|
||||
N --> O[用户在现有表单提交]
|
||||
E -- 否 --> F[登录并保留返回路由和门店]
|
||||
F --> G[创建助手会话]
|
||||
E -- 是 --> G
|
||||
G --> H[文字或语音输入]
|
||||
H --> I[结构化意图提取]
|
||||
I --> J[服务端解析真实宠物和服务]
|
||||
J --> K[查询真实号源]
|
||||
K --> L{草稿完整}
|
||||
L -- 否 --> M[询问一个关键缺失项]
|
||||
M --> H
|
||||
L -- 是 --> N[展示草稿卡]
|
||||
N --> O[带入普通预约]
|
||||
O --> P[CustAppointmentCreate 回填]
|
||||
P --> Q[用户在现有表单提交]
|
||||
```
|
||||
|
||||
### 3.1 入口
|
||||
@ -72,6 +81,8 @@ M0 只在宠主的 `CustAppointmentCreate` 顶部增加一个次级入口:
|
||||
|
||||
该入口不取代当前表单和主提交按钮,不改宠主首页主 CTA。历史预约、报告页和宠物档案深链放到后续迭代。
|
||||
|
||||
M0 不在对话内选择门店。未选门店时先使用现有表单控件;创建 session 后 `storeId` 固定,切店需结束会话并重新创建。
|
||||
|
||||
### 3.2 对话风格
|
||||
|
||||
- 回复简短,一次只问一个关键缺失项;
|
||||
@ -159,22 +170,23 @@ M0 只允许:
|
||||
|
||||
M0 字段:
|
||||
|
||||
- `session_id` VARCHAR(64) 主键;
|
||||
- `id` BIGINT 自增主键;
|
||||
- `session_id` VARCHAR(36) 唯一外部 UUID;
|
||||
- `customer_user_id` BIGINT NOT NULL;
|
||||
- `store_id` BIGINT NULL;
|
||||
- `status` VARCHAR(32) NOT NULL;
|
||||
- `store_id` BIGINT NOT NULL;
|
||||
- `status` VARCHAR(24) NOT NULL;
|
||||
- `draft_json` TEXT NOT NULL;
|
||||
- `draft_version` INT NOT NULL;
|
||||
- `entry_source` VARCHAR(32) NOT NULL;
|
||||
- `input_modality` VARCHAR(16) NULL;
|
||||
- `expires_at` DATETIME NOT NULL;
|
||||
- `create_time` / `update_time`;
|
||||
- `deleted` TINYINT(1) NOT NULL DEFAULT 0。
|
||||
- `create_time` / `update_time`。
|
||||
|
||||
索引:
|
||||
|
||||
- `UNIQUE (session_id)`;
|
||||
- `(customer_user_id, status, expires_at)`;
|
||||
- `(expires_at, deleted)` 用于过期清理。
|
||||
- `(status, expires_at)` 用于过期和物理清理。
|
||||
|
||||
M0 不增加 `appointment_id`,也不建立 `t_booking_agent_turn`。`appointment_id` 唯一关联在 M1 幂等写入评审时再增加,避免 M0 数据模型暗示已具备直接创建能力。
|
||||
|
||||
@ -182,6 +194,8 @@ M0 的 `entry_source` 只允许 `appointment_create`;历史预约、报告、
|
||||
|
||||
`draft_json` 只保存已验证 ID、时间约束和必要备注;不保存每轮用户原文、提示词或模型原始响应。
|
||||
|
||||
TTL 固定为创建后 30 分钟,不滑动续期;过期 24 小时后物理删除。M0 不增加 `deleted`,详细迁移和清理口径以 ADR 为准。
|
||||
|
||||
### 4.5 API 契约
|
||||
|
||||
M0 只实现:
|
||||
@ -190,12 +204,14 @@ M0 只实现:
|
||||
|---|---|---|
|
||||
| `POST` | `/api/booking-agent/sessions` | 创建已登录 customer 会话 |
|
||||
| `POST` | `/api/booking-agent/sessions/{sessionId}/messages` | 提交文字/语音转写文本,刷新草稿 |
|
||||
| `POST` | `/api/booking-agent/transcriptions` | 上传短音频并获取可编辑转写 |
|
||||
| `POST` | `/api/booking-agent/sessions/{sessionId}/transcriptions` | 在本人 session 下上传短音频并获取可编辑转写 |
|
||||
| `POST` | `/api/booking-agent/sessions/{sessionId}/fallback` | 返回可回填普通表单的已验证草稿 |
|
||||
| `DELETE` | `/api/booking-agent/sessions/{sessionId}` | 将本人会话标记为 `cancelled` |
|
||||
|
||||
所有 endpoint 都要求 `CurrentUserContext.require()` 且 role 为 `customer`。每次读写 session 都以 `sessionId + current.userId` 查询;他人 session 和不存在 session 均返回统一 404 语义,避免泄漏 session 是否存在。
|
||||
|
||||
创建 session 时 `storeId` 必填并验证存在;之后不再接受客户端传门店。消息和 fallback 请求必须携带当前 `draftVersion`,版本不一致返回 `DRAFT_VERSION_CONFLICT`。
|
||||
|
||||
M0 绝对不注册 `/confirm` endpoint。
|
||||
|
||||
### 4.6 模型输出强校验
|
||||
@ -214,6 +230,7 @@ M0 绝对不注册 `/confirm` endpoint。
|
||||
"end": null
|
||||
},
|
||||
"remark": null,
|
||||
"clearFields": [],
|
||||
"ambiguities": [],
|
||||
"nextAction": "resolve_context"
|
||||
}
|
||||
@ -221,7 +238,7 @@ M0 绝对不注册 `/confirm` endpoint。
|
||||
|
||||
校验规则:
|
||||
|
||||
- 未知字段拒绝或明确忽略,不得进业务实体;
|
||||
- 未知字段拒绝,不得进业务实体;
|
||||
- `schemaVersion`、`intent` 和 `nextAction` 为枚举;
|
||||
- 文本字段 trim、限长;
|
||||
- 模型输出 ID、价格、号源或预约状态字段时整个响应降级;
|
||||
@ -299,7 +316,7 @@ frontend/src/pages.json # 路由
|
||||
|
||||
## 6. 业务事件与本体
|
||||
|
||||
M0 候选事件:
|
||||
M0 已冻结事件:
|
||||
|
||||
- `booking_agent_started`;
|
||||
- `booking_agent_draft_ready`;
|
||||
@ -307,15 +324,17 @@ M0 候选事件:
|
||||
|
||||
M0 不记录 `booking_agent_confirmed`,也不修改现有 `appointment_created` metadata,因为助手没有直接确认写入,而客户端的 `source` 字符串不能当作服务端归因事实。如后续需要精确联结「助手草稿 -> 表单创建」,另立 brief 设计可验证、一次性的草稿交接机制。
|
||||
|
||||
编码前需增加或更新:
|
||||
Batch 0 已按 `documented` 证据增加:
|
||||
|
||||
- `ontology/objects.md`:`booking_agent_session` gap/implemented 证据;
|
||||
- `ontology/objects.md`:`booking_agent_session` 的 `documented` 模型条目;
|
||||
- `ontology/actions.md`:创建会话、提交消息、语音转写、回填草稿、结束会话;
|
||||
- `ontology/events.md`:M0 三个漏斗事件;
|
||||
- `ontology/rules.md`:customer 数据范围、无写工具、原始语音/原文不持久化、超时降级;
|
||||
- `ontology/relations.md` 与 `graph/ontology.jsonl`;
|
||||
- `coverage/ontology-coverage-audit.md`。
|
||||
|
||||
实现时必须在同一批把真实代码/测试路径补入,并将对应条目从 `documented` 更新为 `anchored`;不得提前宣称已实现。
|
||||
|
||||
## 7. 自动化测试
|
||||
|
||||
### 7.1 后端必测
|
||||
@ -329,6 +348,7 @@ M0 不记录 `booking_agent_confirmed`,也不修改现有 `appointment_created
|
||||
**会话和权限**
|
||||
|
||||
- 未登录、boss/staff 调用助手 endpoint 被拒绝;
|
||||
- 创建 session 缺少/伪造门店被拒绝,创建后不能跨店覆盖;
|
||||
- A customer 无法查询、修改或结束 B customer session;
|
||||
- session 过期后不再接收消息;
|
||||
- M0 不存在 `/confirm` endpoint;
|
||||
@ -369,10 +389,12 @@ M0 不记录 `booking_agent_confirmed`,也不修改现有 `appointment_created
|
||||
|
||||
Owner:System Architect + Data Model + Product Design
|
||||
|
||||
状态:**Completed — 2026-08-02**
|
||||
|
||||
- 冻结 `booking-intent-v1`、M0 状态子集、API shape 和业务码;
|
||||
- 更新本体及机器图谱;
|
||||
- 评审 session 表、TTL 和清理方式;
|
||||
- 完成 Access / Privacy Review。
|
||||
- 完成开发态 Access / Privacy Review;真实 provider 与生产复核仍在 Batch 4。
|
||||
|
||||
### Batch 1:供应商适配与契约测试
|
||||
|
||||
|
||||
295
架构决策-智能预约助手M0契约与数据模型-2026-08-02.md
Normal file
295
架构决策-智能预约助手M0契约与数据模型-2026-08-02.md
Normal file
@ -0,0 +1,295 @@
|
||||
# 架构决策:智能预约助手 M0 契约与数据模型
|
||||
|
||||
> 日期:2026-08-02<br>
|
||||
> 状态:**Accepted for M0 implementation / 尚未实现**<br>
|
||||
> Workstream:`Core Booking Flow`<br>
|
||||
> Track:`Architecture`<br>
|
||||
> Priority:`P0`(独立试验流,不高于 RC6 真实门店基线)<br>
|
||||
> Owner Agent:`System Architect`<br>
|
||||
> Paired QA:`Core Flow QA`<br>
|
||||
> Service Lock:`docs:booking-agent-m0-contract+ontology`<br>
|
||||
> Architecture Review Required:`Yes — completed by this ADR`<br>
|
||||
> Data Model Review Required:`Yes — completed for implementation proposal`<br>
|
||||
> Access / Privacy Review Required:`Yes — development boundary accepted; live provider and production review pending`<br>
|
||||
> RC Included:`No`
|
||||
|
||||
**关联资产**
|
||||
|
||||
- [产品与技术方案](./宠小它智能预约助手-产品与技术方案-v0.1.md)
|
||||
- [M0 编码任务 brief](./智能预约助手-M0编码任务brief-2026-08-02.md)
|
||||
- [M0 执行队列](./智能预约助手-M0执行队列-2026-08-02.md)
|
||||
- [`booking-intent-v1` JSON Schema](./contracts/booking-intent-v1.schema.json)
|
||||
- [M0 OpenAPI 契约](./contracts/booking-agent-m0.openapi.yaml)
|
||||
- [模型与语音供应商选型](./智能预约助手-模型与语音供应商选型-2026-08-02.md)
|
||||
|
||||
## 1. 决策结论
|
||||
|
||||
M0 冻结为 **「已登录 customer 在已选门店范围内,用文字或语音生成已验证预约草稿,再回填现有普通预约表单」**。
|
||||
|
||||
冻结边界:
|
||||
|
||||
1. 门店不是模型槽位。宠主必须先在 `CustAppointmentCreate` 选定门店,创建会话时只提交 `storeId`,服务端验证门店存在。
|
||||
2. 模型只输出通过 `booking-intent-v1` 校验的非权威文本约束,不输出或决定业务 ID、价格、服务事实、号源或预约状态。
|
||||
3. 宠物、服务、日期时间和号源全部由 Petstore 后端在 `current.userId + session.storeId` 范围内重新解析。
|
||||
4. M0 没有 `/confirm` endpoint,不调用 `AppointmentService.createBooking`,不修改 `Appointment` 表或状态机。
|
||||
5. 「带入普通预约」只返回一次页面级回填数据;最终仍由现有 `/api/appointment/create` 提交。
|
||||
6. 原始语音、当轮用户原文、模型请求/响应和完整对话不落库、不进业务事件、不进日志。
|
||||
7. 功能开关默认关闭,不进入 `phase2-pilot-rc6`。
|
||||
|
||||
## 2. 为什么先选门店
|
||||
|
||||
原方案允许助手在对话内收集门店,但会同时引入门店名称歧义、地理推荐、跨店服务匹配和无门店 `BusinessEvent` 等问题。当前产品仍是单店 SaaS 试点,不需要把门店发现变成 Agent 能力。
|
||||
|
||||
M0 的次级入口仍位于 `CustAppointmentCreate`,行为调整为:
|
||||
|
||||
1. 未登录:先登录并返回原预约页;
|
||||
2. 未选门店:先使用现有门店选择控件;
|
||||
3. 已选有效门店:创建智能会话;
|
||||
4. 会话内只解析该门店的服务与容量。
|
||||
|
||||
这样可以让每个 session 和漏斗事件都有明确 `storeId`,并阻止模型扩大门店数据范围。
|
||||
|
||||
## 3. `booking-intent-v1` 冻结
|
||||
|
||||
模型输出以 [`contracts/booking-intent-v1.schema.json`](./contracts/booking-intent-v1.schema.json) 为唯一机器契约。
|
||||
|
||||
### 3.1 字段语义
|
||||
|
||||
| 字段 | 语义 | 是否业务权威 |
|
||||
|---|---|---|
|
||||
| `schemaVersion` | 固定 `booking-intent-v1` | 是,仅契约版本 |
|
||||
| `intent` | `book / modify / end / fallback` | 否,状态机重新判定 |
|
||||
| `petQuery` | 宠物展示名搜索词 | 否 |
|
||||
| `serviceQuery` | 服务名称/同义表达搜索词 | 否 |
|
||||
| `dateExpression` | 用户日期表达 | 否 |
|
||||
| `timeWindow` | `HH:mm` 起止偏好 | 否 |
|
||||
| `remark` | 用户希望带入预约的短备注 | 否,可编辑且限 200 字 |
|
||||
| `clearFields` | 用户明确要求清空的草稿字段 | 否,服务端只允许白名单字段 |
|
||||
| `ambiguities` | 模型观察到的歧义类型 | 否,服务端重新确认 |
|
||||
| `nextAction` | 下一步建议 | 否,编排器可忽略 |
|
||||
|
||||
### 3.2 强校验顺序
|
||||
|
||||
1. 响应体必须是单个 JSON object,不接受 Markdown fence 或前后说明文字;
|
||||
2. JSON Schema 校验通过,未知字段拒绝;
|
||||
3. 文本 trim、控制字符过滤和长度校验;
|
||||
4. 输出出现任意 ID、价格、手机号、号源或预约状态字段时整个响应降级;
|
||||
5. 服务端时间解析、归属解析和号源查询成功后才更新 `BookingDraft`;
|
||||
6. 失败不尝试从残缺 JSON 猜字段,返回 `AGENT_UNAVAILABLE` 并保留旧草稿。
|
||||
|
||||
### 3.3 时间口径
|
||||
|
||||
- 时区固定 `Asia/Shanghai`;
|
||||
- 支持绝对日期、今天/明天/后天、本周 X/下周 X、精确时间、上午/下午、X 点以前/以后;
|
||||
- 「过几天」「最近」「周末都行」等表达不得猜成绝对日期,必须返回日期选择项;
|
||||
- `appointmentTime` 只有在现有容量服务返回真实 slot 后才能写入草稿;
|
||||
- 对外时间字符串统一为 `yyyy-MM-dd'T'HH:mm:ss`,语义为 `Asia/Shanghai` 本地时间。
|
||||
|
||||
## 4. M0 会话状态机
|
||||
|
||||
```mermaid
|
||||
stateDiagram-v2
|
||||
[*] --> collecting: create session
|
||||
collecting --> collecting: 信息仍不足
|
||||
collecting --> proposing: 可搜索真实号源
|
||||
proposing --> collecting: 修改条件或需要澄清
|
||||
proposing --> confirmable: 选择真实 slot
|
||||
confirmable --> collecting: 用户修改草稿
|
||||
collecting --> fallback: 用户回填表单或 LLM 降级
|
||||
proposing --> fallback: 用户回填表单或 LLM 降级
|
||||
confirmable --> fallback: 带入普通预约
|
||||
collecting --> cancelled: 用户结束
|
||||
proposing --> cancelled: 用户结束
|
||||
confirmable --> cancelled: 用户结束
|
||||
collecting --> expired: TTL 到期
|
||||
proposing --> expired: TTL 到期
|
||||
confirmable --> expired: TTL 到期
|
||||
```
|
||||
|
||||
不允许的状态:`submitting`、`booked`、`needs_reselection`。终态 `fallback / expired / cancelled` 不接受新消息或转写。
|
||||
|
||||
每次消息请求必须带客户端当前 `draftVersion`。服务端只在版本相等时更新,成功后加一;版本冲突返回 `DRAFT_VERSION_CONFLICT`,不得静默覆盖。
|
||||
|
||||
## 5. API 冻结
|
||||
|
||||
唯一契约是 [`contracts/booking-agent-m0.openapi.yaml`](./contracts/booking-agent-m0.openapi.yaml)。M0 只注册五个 endpoint:
|
||||
|
||||
| Method | Endpoint | 结果 |
|
||||
|---|---|---|
|
||||
| `POST` | `/api/booking-agent/sessions` | 在已选 `storeId` 下创建 customer 会话 |
|
||||
| `POST` | `/api/booking-agent/sessions/{sessionId}/messages` | 提交当轮已确认文字并刷新草稿 |
|
||||
| `POST` | `/api/booking-agent/sessions/{sessionId}/transcriptions` | 转写短音频,文字返回输入框,不自动发送 |
|
||||
| `POST` | `/api/booking-agent/sessions/{sessionId}/fallback` | 返回已验证的部分/完整草稿并结束会话 |
|
||||
| `DELETE` | `/api/booking-agent/sessions/{sessionId}` | 结束本人会话 |
|
||||
|
||||
### 5.1 身份与资源隐藏
|
||||
|
||||
- 所有 endpoint 都必须经过 `AuthInterceptor`;
|
||||
- controller 再要求 `current.role == customer`,boss/staff 返回 `FORBIDDEN`;
|
||||
- session 每次以 `sessionId + current.userId` 查询;不存在和他人 session 统一返回 `SESSION_NOT_FOUND`;
|
||||
- `storeId` 只在创建时接收并验证,后续从 session 读取;
|
||||
- 音频转写 endpoint 必须挂在 session 下,客户端不得上传宠物/服务词表。
|
||||
|
||||
### 5.2 响应语义
|
||||
|
||||
沿用 `{ code, message?, bizCode?, data? }`。当前前端以 body `code` 判断业务结果;除 `AuthInterceptor` 的 HTTP 401 外,M0 不借机重构全局 HTTP status 行为。
|
||||
|
||||
草稿中的 `storeId / petId / serviceTypeId / appointmentTime` 是经过后端重新解析的事实;客户端回填后,现有预约创建服务仍会重新执行最终权限、服务和容量校验。
|
||||
|
||||
### 5.3 业务码
|
||||
|
||||
| body code | bizCode | 语义 |
|
||||
|---|---|---|
|
||||
| 401 | `UNAUTHENTICATED` | session token 缺失或失效 |
|
||||
| 403 | `FORBIDDEN` | 非 customer 调用 |
|
||||
| 503 | `AGENT_DISABLED` | 功能开关关闭 |
|
||||
| 404 | `STORE_NOT_FOUND` | 创建时目标门店无效 |
|
||||
| 404 | `SESSION_NOT_FOUND` | session 不存在或不属于当前 customer |
|
||||
| 410 | `SESSION_EXPIRED` | 会话 TTL 到期 |
|
||||
| 409 | `SESSION_TERMINAL` | 会话已 fallback/cancelled |
|
||||
| 409 | `DRAFT_VERSION_CONFLICT` | 客户端草稿版本过旧 |
|
||||
| 400 | `INVALID_INPUT` | 空文本、超长或请求结构无效 |
|
||||
| 400 | `INVALID_AUDIO` | 空音频、格式/MIME/文件头不匹配或时长非法 |
|
||||
| 413 | `AUDIO_TOO_LARGE` | 超过 3 MB |
|
||||
| 429 | `RATE_LIMITED` | customer 请求频率超限 |
|
||||
| 503 | `AGENT_UNAVAILABLE` | LLM 超时、无效 JSON/schema 或外部失败 |
|
||||
| 503 | `ASR_UNAVAILABLE` | ASR 超时或外部失败 |
|
||||
|
||||
宠物/服务歧义、无号源和未选时段是正常 `200` 会话响应,不用异常码表达。
|
||||
|
||||
## 6. 数据模型冻结
|
||||
|
||||
计划迁移:`backend/db/migrations/20260802_create_booking_agent_session.sql`。Batch 0 只冻结设计,不在 docs 任务内创建迁移。
|
||||
|
||||
### 6.1 `t_booking_agent_session`
|
||||
|
||||
| 字段 | 类型 | 规则 |
|
||||
|---|---|---|
|
||||
| `id` | BIGINT | 自增主键,只在后端和 `BusinessEvent.aggregate_id` 使用 |
|
||||
| `session_id` | VARCHAR(36) | UUID,唯一,对客户端暴露 |
|
||||
| `customer_user_id` | BIGINT | 必须是创建会话的 customer |
|
||||
| `store_id` | BIGINT | M0 必填;创建时验证存在,后续不接受覆盖 |
|
||||
| `status` | VARCHAR(24) | 仅六个 M0 状态 |
|
||||
| `draft_json` | TEXT | 最小已验证草稿,不存当轮原文/模型响应 |
|
||||
| `draft_version` | INT | 初始 0,每次成功草稿更新加一 |
|
||||
| `entry_source` | VARCHAR(32) | M0 固定 `appointment_create` |
|
||||
| `input_modality` | VARCHAR(16) | `text / voice / mixed`,首次成功消息后派生 |
|
||||
| `expires_at` | DATETIME | 创建时固定 `now + 30min`,M0 不滑动续期 |
|
||||
| `create_time / update_time` | DATETIME | 服务端时间 |
|
||||
|
||||
索引:
|
||||
|
||||
- `UNIQUE uk_booking_agent_session_public_id (session_id)`;
|
||||
- `INDEX idx_booking_agent_customer_status_expire (customer_user_id, status, expires_at)`;
|
||||
- `INDEX idx_booking_agent_status_expire (status, expires_at)`。
|
||||
|
||||
不增加:
|
||||
|
||||
- `appointment_id`;
|
||||
- `t_booking_agent_turn`;
|
||||
- 原始语音/转写/提示词/模型响应列;
|
||||
- `deleted`。会话是短期对象,使用明确终态并按 TTL 物理清理。
|
||||
|
||||
### 6.2 TTL 与清理
|
||||
|
||||
1. 会话固定 30 分钟有效,不因消息滑动延长;
|
||||
2. endpoint 发现过期时返回 `SESSION_EXPIRED`,并以幂等方式标记 `expired`;
|
||||
3. 每小时清理任务将到期活动会话标记 `expired`;
|
||||
4. `expires_at` 早于当前时间 24 小时的 session 物理删除;
|
||||
5. 清理失败记录数量和错误类别,不记录 `draft_json`;
|
||||
6. `BusinessEvent` 是独立低敏事实,不随短期 session 删除。
|
||||
|
||||
## 7. 业务事件冻结
|
||||
|
||||
M0 只新增三个首次漏斗事实:
|
||||
|
||||
| 事件 | 触发 | 幂等键 | metadata 白名单 |
|
||||
|---|---|---|---|
|
||||
| `booking_agent_started` | session 创建成功 | `booking_agent_started:{sessionDbId}` | `entrySource` |
|
||||
| `booking_agent_draft_ready` | session 首次进入 `confirmable` | `booking_agent_draft_ready:{sessionDbId}` | `inputModality` |
|
||||
| `booking_agent_fallback` | 用户回填表单或 LLM 降级 | `booking_agent_fallback:{sessionDbId}` | `reason=user/llm_unavailable` |
|
||||
|
||||
事件使用:
|
||||
|
||||
- `storeId=session.storeId`;
|
||||
- `aggregateType=booking_agent_session`;
|
||||
- `aggregateId=session.id`;
|
||||
- `actorUserId=session.customerUserId`、`actorRole=customer`、`source=customer`;
|
||||
- 已有 `StoreCustomer` 时可关联,不能只为启动助手创建客户主档。
|
||||
|
||||
禁止把输入原文、转写文本、备注、宠物名、服务名、供应商 request ID 或错误原文写入 metadata。
|
||||
|
||||
M0 不写 `booking_agent_confirmed`,也不修改现有 `appointment_created` metadata。精确联结「助手草稿 -> 表单创建」需要另行设计服务端一次性交接机制。
|
||||
|
||||
## 8. 外部调用与隐私门禁
|
||||
|
||||
### 8.1 语音
|
||||
|
||||
- 单段不超过 60 秒、3 MB;客户端预检,服务端按字节、文件头、MIME 和解析时长复核;
|
||||
- ASR 使用 `qwen3-asr-flash-2026-02-10` Base64 Data URL,不中转 OSS;
|
||||
- 上下文只包含当前门店服务名和当前 customer 的宠物展示名;
|
||||
- 返回转写后立即释放音频字节,不缓存、不写文件、不返回供应商 request ID;
|
||||
- 转写必须由用户编辑/确认后才能调用 messages。
|
||||
|
||||
### 8.2 LLM
|
||||
|
||||
- 使用固定 `qwen-plus-2025-12-01`、非思考、非流式、无工具、无联网;
|
||||
- LLM 超时 3 秒,ASR 超时 5 秒;不做多次自动重试;
|
||||
- 请求只包含当次文字、最小草稿和枚举说明,不包含手机号、token、客户时间线或其他门店数据;
|
||||
- API Key、Workspace ID 和 Authorization header 不进 Git、测试 fixture、聊天、截图或日志。
|
||||
|
||||
### 8.3 频率门禁
|
||||
|
||||
M0 默认每个 customer:
|
||||
|
||||
- 10 分钟内最多创建 5 个 session;
|
||||
- 10 分钟内最多提交 30 条 message;
|
||||
- 10 分钟内最多转写 10 段音频。
|
||||
|
||||
超限返回 `RATE_LIMITED`,普通预约继续可用。当前单实例可先使用进程内限流;进入多实例前必须切换共享限流状态。
|
||||
|
||||
## 9. Review 结论
|
||||
|
||||
### Architecture Review
|
||||
|
||||
**通过,可进入实现。** 条件是严格保留五个 endpoint、无 `/confirm`、门店先选、普通表单独立可用、feature flag 默认关闭。
|
||||
|
||||
### Data Model Review
|
||||
|
||||
**通过提案,可由 Backend Core 实现迁移。** 相比原 brief 的修订:
|
||||
|
||||
- 使用 `id BIGINT` 内部主键 + `session_id UUID` 外部唯一键;
|
||||
- `store_id` 从可空改为 M0 必填;
|
||||
- 去掉 `deleted` 和 `appointment_id`;
|
||||
- 增加固定 TTL、物理清理和 `draft_version` 并发规则。
|
||||
|
||||
### Access / Privacy Review
|
||||
|
||||
**开发边界通过,真实供应商 smoke 与生产开启未通过。** 不需要真实密钥即可实现接口、stub、状态机和测试;真实调用前仍需完成供应商数据处理确认、小程序隐私告知、费用告警和已知情语音样本评测。
|
||||
|
||||
## 10. 后果与风险
|
||||
|
||||
正向结果:
|
||||
|
||||
- M0 不增加第二条预约写路径,模型失败不会污染 `Appointment`;
|
||||
- session 可短期恢复结构化草稿,又不形成对话原文数据库;
|
||||
- OpenAPI、JSON Schema 和本体可以直接驱动测试与 review;
|
||||
- 后续 M1 必须另立 ADR 才能新增 confirm/appointment 关联。
|
||||
|
||||
已知限制:
|
||||
|
||||
- 门店必须先选,无法用一句话同时完成「找店 + 预约」;
|
||||
- M0 不精确统计回填后最终创建转化;
|
||||
- 进程内限流只适用于当前单实例;
|
||||
- 供应商效果仍需真实语料验证,当前推荐不是生产准入结论。
|
||||
|
||||
## 11. 实现门禁
|
||||
|
||||
编码 PR 必须同时满足:
|
||||
|
||||
- 引用本 ADR、OpenAPI、JSON Schema 和对应 `ontologyRefs`;
|
||||
- provider 单测只访问本地 HTTP stub;
|
||||
- 后端测试证明整个 M0 不调用 `AppointmentMapper.save`;
|
||||
- 未配置密钥或 feature flag 关闭时应用正常启动、普通预约正常;
|
||||
- 本体从 `documented` 更新为 `anchored` 只能与代码和测试同一批完成;
|
||||
- 真实供应商 smoke、生产配置和 RC 纳入必须重新授权。
|
||||
Loading…
Reference in New Issue
Block a user