From c432f457b8da859f030284d8a96e50a3aaa58258 Mon Sep 17 00:00:00 2001 From: MerCry Date: Mon, 2 Mar 2026 22:14:46 +0800 Subject: [PATCH] feat: implement metadata field definition with status governance [AC-IDSMETA-13, AC-IDSMETA-14] --- .../src/api/decomposition-template.ts | 59 ++ ai-service-admin/src/api/metadata-schema.ts | 59 ++ .../metadata/MetadataFieldRenderer.vue | 189 +++++ .../src/components/metadata/MetadataForm.vue | 224 ++++++ .../components/metadata/TypeChangeHandler.vue | 232 ++++++ .../src/components/metadata/index.ts | 3 + ai-service-admin/src/router/index.ts | 12 + .../src/types/decomposition-template.ts | 84 +++ ai-service-admin/src/types/intent-rule.ts | 5 + ai-service-admin/src/types/metadata.ts | 93 +++ ai-service-admin/src/types/prompt-template.ts | 11 +- ai-service-admin/src/types/script-flow.ts | 33 +- .../admin/decomposition-template/index.vue | 676 ++++++++++++++++++ .../src/views/admin/intent-rule/index.vue | 46 +- .../components/DocumentList.vue | 323 +++++++-- .../src/views/admin/metadata-schema/index.vue | 587 +++++++++++++++ .../admin/monitoring/ConversationTracking.vue | 4 +- .../src/views/admin/prompt-template/index.vue | 51 +- .../components/ConstraintManager.vue | 142 ++++ .../src/views/admin/script-flow/index.vue | 321 ++++++++- ai-service/app/api/admin/__init__.py | 6 + .../api/admin/metadata_field_definition.py | 360 ++++++++++ ai-service/app/main.py | 6 + ai-service/app/models/entities.py | 329 ++++++++- .../metadata_field_definition_service.py | 472 ++++++++++++ 25 files changed, 4244 insertions(+), 83 deletions(-) create mode 100644 ai-service-admin/src/api/decomposition-template.ts create mode 100644 ai-service-admin/src/api/metadata-schema.ts create mode 100644 ai-service-admin/src/components/metadata/MetadataFieldRenderer.vue create mode 100644 ai-service-admin/src/components/metadata/MetadataForm.vue create mode 100644 ai-service-admin/src/components/metadata/TypeChangeHandler.vue create mode 100644 ai-service-admin/src/components/metadata/index.ts create mode 100644 ai-service-admin/src/types/decomposition-template.ts create mode 100644 ai-service-admin/src/types/metadata.ts create mode 100644 ai-service-admin/src/views/admin/decomposition-template/index.vue create mode 100644 ai-service-admin/src/views/admin/metadata-schema/index.vue create mode 100644 ai-service-admin/src/views/admin/script-flow/components/ConstraintManager.vue create mode 100644 ai-service/app/api/admin/metadata_field_definition.py create mode 100644 ai-service/app/services/metadata_field_definition_service.py diff --git a/ai-service-admin/src/api/decomposition-template.ts b/ai-service-admin/src/api/decomposition-template.ts new file mode 100644 index 0000000..ad7a860 --- /dev/null +++ b/ai-service-admin/src/api/decomposition-template.ts @@ -0,0 +1,59 @@ +import request from '@/utils/request' +import type { + DecompositionTemplate, + DecompositionTemplateDetail, + DecompositionTemplateCreate, + DecompositionTemplateUpdate, + DecompositionTemplateListResponse +} from '@/types/decomposition-template' + +export const decompositionTemplateApi = { + list: (params?: { scene?: string; status?: string }) => + request({ + method: 'GET', + url: '/admin/decomposition-templates', + params + }), + + get: (id: string) => + request({ + method: 'GET', + url: `/admin/decomposition-templates/${id}` + }), + + create: (data: DecompositionTemplateCreate) => + request({ + method: 'POST', + url: '/admin/decomposition-templates', + data + }), + + update: (id: string, data: DecompositionTemplateUpdate) => + request({ + method: 'PUT', + url: `/admin/decomposition-templates/${id}`, + data + }), + + delete: (id: string) => + request({ method: 'DELETE', url: `/admin/decomposition-templates/${id}` }), + + activate: (id: string) => + request({ + method: 'POST', + url: `/admin/decomposition-templates/${id}/activate` + }), + + archive: (id: string) => + request({ + method: 'POST', + url: `/admin/decomposition-templates/${id}/archive` + }), + + rollback: (id: string, version: number) => + request({ + method: 'POST', + url: `/admin/decomposition-templates/${id}/rollback`, + data: { version } + }) +} diff --git a/ai-service-admin/src/api/metadata-schema.ts b/ai-service-admin/src/api/metadata-schema.ts new file mode 100644 index 0000000..b091a4a --- /dev/null +++ b/ai-service-admin/src/api/metadata-schema.ts @@ -0,0 +1,59 @@ +import request from '@/utils/request' +import type { + MetadataFieldDefinition, + MetadataFieldCreateRequest, + MetadataFieldUpdateRequest, + MetadataFieldListResponse, + MetadataPayload, + MetadataScope +} from '@/types/metadata' + +export const metadataSchemaApi = { + list: (status?: 'draft' | 'active' | 'deprecated') => + request({ method: 'GET', url: '/admin/metadata-schemas', params: status ? { status } : {} }), + + get: (id: string) => + request({ method: 'GET', url: `/admin/metadata-schemas/${id}` }), + + create: (data: MetadataFieldCreateRequest) => + request({ method: 'POST', url: '/admin/metadata-schemas', data }), + + update: (id: string, data: MetadataFieldUpdateRequest) => + request({ method: 'PUT', url: `/admin/metadata-schemas/${id}`, data }), + + delete: (id: string) => + request({ method: 'DELETE', url: `/admin/metadata-schemas/${id}` }), + + getByScope: (scope: MetadataScope, includeDeprecated = false) => + request({ + method: 'GET', + url: '/admin/metadata-schemas', + params: { scope, include_deprecated: includeDeprecated } + }), + + validate: (metadata: MetadataPayload, scope?: MetadataScope) => + request<{ valid: boolean; errors?: { field_key: string; message: string }[] }>({ + method: 'POST', + url: '/admin/metadata-schemas/validate', + data: { metadata, scope } + }), + + checkCompatibility: (oldScope: MetadataScope, newScope: MetadataScope, metadata: MetadataPayload) => + request<{ + compatible: boolean; + conflicts: { field_key: string; reason: string }[]; + preserved_keys: string[] + }>({ + method: 'POST', + url: '/admin/metadata-schemas/check-compatibility', + data: { old_scope: oldScope, new_scope: newScope, metadata } + }) +} + +export type { + MetadataFieldDefinition, + MetadataFieldCreateRequest, + MetadataFieldUpdateRequest, + MetadataFieldListResponse, + MetadataPayload +} diff --git a/ai-service-admin/src/components/metadata/MetadataFieldRenderer.vue b/ai-service-admin/src/components/metadata/MetadataFieldRenderer.vue new file mode 100644 index 0000000..9ce23d0 --- /dev/null +++ b/ai-service-admin/src/components/metadata/MetadataFieldRenderer.vue @@ -0,0 +1,189 @@ + + + + + diff --git a/ai-service-admin/src/components/metadata/MetadataForm.vue b/ai-service-admin/src/components/metadata/MetadataForm.vue new file mode 100644 index 0000000..480a060 --- /dev/null +++ b/ai-service-admin/src/components/metadata/MetadataForm.vue @@ -0,0 +1,224 @@ + + + + + diff --git a/ai-service-admin/src/components/metadata/TypeChangeHandler.vue b/ai-service-admin/src/components/metadata/TypeChangeHandler.vue new file mode 100644 index 0000000..9819211 --- /dev/null +++ b/ai-service-admin/src/components/metadata/TypeChangeHandler.vue @@ -0,0 +1,232 @@ + + + + + diff --git a/ai-service-admin/src/components/metadata/index.ts b/ai-service-admin/src/components/metadata/index.ts new file mode 100644 index 0000000..d5a96ee --- /dev/null +++ b/ai-service-admin/src/components/metadata/index.ts @@ -0,0 +1,3 @@ +export { default as MetadataForm } from './MetadataForm.vue' +export { default as MetadataFieldRenderer } from './MetadataFieldRenderer.vue' +export { default as TypeChangeHandler } from './TypeChangeHandler.vue' diff --git a/ai-service-admin/src/router/index.ts b/ai-service-admin/src/router/index.ts index 93b5470..b4b58e5 100644 --- a/ai-service-admin/src/router/index.ts +++ b/ai-service-admin/src/router/index.ts @@ -53,6 +53,12 @@ const routes: Array = [ component: () => import('@/views/admin/knowledge-base/index.vue'), meta: { title: '多知识库管理' } }, + { + path: '/admin/metadata-schemas', + name: 'MetadataSchema', + component: () => import('@/views/admin/metadata-schema/index.vue'), + meta: { title: '元数据模式配置' } + }, { path: '/admin/intent-rules', name: 'IntentRule', @@ -71,6 +77,12 @@ const routes: Array = [ component: () => import('@/views/admin/guardrail/index.vue'), meta: { title: '输出护栏管理' } }, + { + path: '/admin/decomposition-templates', + name: 'DecompositionTemplate', + component: () => import('@/views/admin/decomposition-template/index.vue'), + meta: { title: '拆解模板管理' } + }, { path: '/admin/monitoring/intent-rules', name: 'IntentRuleMonitoring', diff --git a/ai-service-admin/src/types/decomposition-template.ts b/ai-service-admin/src/types/decomposition-template.ts new file mode 100644 index 0000000..dfdea74 --- /dev/null +++ b/ai-service-admin/src/types/decomposition-template.ts @@ -0,0 +1,84 @@ +import type { MetadataPayload } from '@/types/metadata' + +export interface DecompositionTemplate { + id: string + name: string + scene: string + description?: string + current_version: number + status: DecompositionStatus + is_latest_effective: boolean + effective_at?: string + metadata?: MetadataPayload + created_at: string + updated_at: string +} + +export interface DecompositionTemplateDetail { + id: string + name: string + scene: string + description?: string + current_version: number + status: DecompositionStatus + is_latest_effective: boolean + effective_at?: string + steps: DecompositionStep[] + versions: DecompositionVersion[] + metadata?: MetadataPayload + created_at: string + updated_at: string +} + +export type DecompositionStatus = 'draft' | 'active' | 'archived' + +export interface DecompositionStep { + step_id: string + step_no: number + instruction: string + expected_output?: string + dependencies?: string[] +} + +export interface DecompositionVersion { + version: number + status: DecompositionStatus + steps: DecompositionStep[] + created_at: string + effective_at?: string + archived_at?: string +} + +export interface DecompositionTemplateCreate { + name: string + scene: string + description?: string + steps: DecompositionStep[] + metadata?: MetadataPayload +} + +export interface DecompositionTemplateUpdate { + name?: string + scene?: string + description?: string + steps?: DecompositionStep[] + metadata?: MetadataPayload +} + +export interface DecompositionTemplateListResponse { + data: DecompositionTemplate[] +} + +export const DECOMPOSITION_STATUS_OPTIONS = [ + { value: 'draft', label: '草稿', color: 'info' }, + { value: 'active', label: '生效', color: 'success' }, + { value: 'archived', label: '归档', color: 'warning' } +] + +export const DECOMPOSITION_SCENE_OPTIONS = [ + { value: 'customer_service', label: '客服场景' }, + { value: 'sales', label: '销售场景' }, + { value: 'support', label: '技术支持' }, + { value: 'complaint', label: '投诉处理' }, + { value: 'general', label: '通用场景' } +] diff --git a/ai-service-admin/src/types/intent-rule.ts b/ai-service-admin/src/types/intent-rule.ts index fd6712c..e4f154b 100644 --- a/ai-service-admin/src/types/intent-rule.ts +++ b/ai-service-admin/src/types/intent-rule.ts @@ -1,3 +1,5 @@ +import type { MetadataPayload } from '@/types/metadata' + export interface IntentRule { id: string name: string @@ -11,6 +13,7 @@ export interface IntentRule { transfer_message?: string hit_count: number is_enabled: boolean + metadata?: MetadataPayload created_at: string updated_at: string } @@ -26,6 +29,7 @@ export interface IntentRuleCreate { flow_id?: string transfer_message?: string is_enabled?: boolean + metadata?: MetadataPayload } export interface IntentRuleUpdate { @@ -39,6 +43,7 @@ export interface IntentRuleUpdate { flow_id?: string transfer_message?: string is_enabled?: boolean + metadata?: MetadataPayload } export interface IntentRuleListResponse { diff --git a/ai-service-admin/src/types/metadata.ts b/ai-service-admin/src/types/metadata.ts new file mode 100644 index 0000000..ef48250 --- /dev/null +++ b/ai-service-admin/src/types/metadata.ts @@ -0,0 +1,93 @@ +export type MetadataFieldType = 'string' | 'number' | 'boolean' | 'enum' | 'array_enum' +export type MetadataFieldStatus = 'draft' | 'active' | 'deprecated' +export type MetadataScope = 'kb_document' | 'intent_rule' | 'script_flow' | 'prompt_template' + +export interface MetadataFieldDefinition { + id: string + field_key: string + label: string + type: MetadataFieldType + description?: string + required: boolean + options?: string[] + default?: string | number | boolean + scope: MetadataScope[] + is_filterable: boolean + is_rank_feature: boolean + status: MetadataFieldStatus + created_at?: string + updated_at?: string +} + +export interface MetadataFieldCreateRequest { + field_key: string + label: string + type: MetadataFieldType + required: boolean + options?: string[] + default?: string | number | boolean + scope: MetadataScope[] + is_filterable?: boolean + is_rank_feature?: boolean + status: MetadataFieldStatus +} + +export interface MetadataFieldUpdateRequest { + label?: string + required?: boolean + options?: string[] + default?: string | number | boolean + scope?: MetadataScope[] + is_filterable?: boolean + is_rank_feature?: boolean + status?: MetadataFieldStatus +} + +export interface MetadataFieldListResponse { + items: MetadataFieldDefinition[] +} + +export interface MetadataPayload { + [key: string]: string | number | boolean | string[] | undefined +} + +export interface TypeChangeConflict { + field_key: string + label: string + conflict_type: 'removed' | 'type_mismatch' + old_value?: string | number | boolean | string[] + suggested_action: 'remove' | 'map' + map_to?: string +} + +export interface TypeChangeResult { + preserved: { field_key: string; value: string | number | boolean | string[] | undefined }[] + conflicts: TypeChangeConflict[] +} + +export const METADATA_STATUS_OPTIONS = [ + { value: 'draft', label: '草稿', color: 'info', description: '字段可编辑,不可用于新建对象' }, + { value: 'active', label: '生效', color: 'success', description: '可用于新建与编辑对象' }, + { value: 'deprecated', label: '废弃', color: 'danger', description: '不可用于新建,历史数据可读' } +] + +export const METADATA_SCOPE_OPTIONS = [ + { value: 'kb_document', label: '知识库文档' }, + { value: 'intent_rule', label: '意图规则' }, + { value: 'script_flow', label: '话术流程' }, + { value: 'prompt_template', label: 'Prompt模板' } +] + +export const METADATA_TYPE_OPTIONS = [ + { value: 'string', label: '文本' }, + { value: 'number', label: '数字' }, + { value: 'boolean', label: '布尔值' }, + { value: 'enum', label: '单选枚举' }, + { value: 'array_enum', label: '多选枚举' } +] + +export const STATUS_TAG_MAP: Record = { + draft: 'info', + active: 'success', + deprecated: 'danger' +} diff --git a/ai-service-admin/src/types/prompt-template.ts b/ai-service-admin/src/types/prompt-template.ts index 18eeb49..f59450e 100644 --- a/ai-service-admin/src/types/prompt-template.ts +++ b/ai-service-admin/src/types/prompt-template.ts @@ -1,9 +1,12 @@ +import type { MetadataPayload } from '@/types/metadata' + export interface PromptTemplate { id: string name: string scene: string description?: string is_default: boolean + metadata?: MetadataPayload published_version?: PromptVersionInfo created_at: string updated_at: string @@ -24,6 +27,7 @@ export interface PromptTemplateDetail { current_content?: string variables?: PromptVariable[] versions?: PromptVersion[] + metadata?: MetadataPayload published_version?: PromptVersionInfo created_at: string updated_at: string @@ -51,6 +55,7 @@ export interface PromptTemplateCreate { system_instruction: string variables?: PromptVariable[] is_default?: boolean + metadata?: MetadataPayload } export interface PromptTemplateUpdate { @@ -59,6 +64,7 @@ export interface PromptTemplateUpdate { description?: string system_instruction?: string variables?: PromptVariable[] + metadata?: MetadataPayload } export interface PromptTemplateListResponse { @@ -84,8 +90,11 @@ export const SCENE_OPTIONS = [ export const BUILTIN_VARIABLES: PromptVariable[] = [ { name: 'persona_name', description: 'AI 人设名称', default_value: 'AI助手' }, + { name: 'persona_personality', description: 'AI 性格特点', default_value: '热情、耐心、专业' }, + { name: 'persona_tone', description: 'AI 说话风格', default_value: '亲切自然,使用口语化表达' }, + { name: 'brand_name', description: '品牌名称', default_value: '我们公司' }, { name: 'current_time', description: '当前时间' }, - { name: 'channel_type', description: '渠道类型(web/wechat/app)' }, + { name: 'channel_type', description: '渠道类型(web/wechat/phone/app)' }, { name: 'user_name', description: '用户名称' }, { name: 'context', description: '检索上下文' }, { name: 'query', description: '用户问题' }, diff --git a/ai-service-admin/src/types/script-flow.ts b/ai-service-admin/src/types/script-flow.ts index b85e3b3..91b4584 100644 --- a/ai-service-admin/src/types/script-flow.ts +++ b/ai-service-admin/src/types/script-flow.ts @@ -1,3 +1,5 @@ +import type { MetadataPayload } from '@/types/metadata' + export interface ScriptFlow { id: string name: string @@ -5,6 +7,7 @@ export interface ScriptFlow { step_count: number is_enabled: boolean linked_rule_count: number + metadata?: MetadataPayload created_at: string updated_at: string } @@ -15,10 +18,13 @@ export interface ScriptFlowDetail { description?: string steps: FlowStep[] is_enabled: boolean + metadata?: MetadataPayload created_at: string updated_at: string } +export type ScriptMode = 'fixed' | 'flexible' | 'template' + export interface FlowStep { step_id: string step_no: number @@ -27,11 +33,18 @@ export interface FlowStep { timeout_seconds?: number timeout_action?: 'repeat' | 'skip' | 'transfer' next_conditions?: NextCondition[] + default_next?: number + script_mode?: ScriptMode + intent?: string + intent_description?: string + script_constraints?: string[] + expected_variables?: string[] } export interface NextCondition { - keywords: string[] - target_step_id: string + keywords?: string[] + pattern?: string + goto_step: number } export interface ScriptFlowCreate { @@ -39,6 +52,7 @@ export interface ScriptFlowCreate { description?: string steps: FlowStep[] is_enabled?: boolean + metadata?: MetadataPayload } export interface ScriptFlowUpdate { @@ -46,6 +60,7 @@ export interface ScriptFlowUpdate { description?: string steps?: FlowStep[] is_enabled?: boolean + metadata?: MetadataPayload } export interface ScriptFlowListResponse { @@ -57,3 +72,17 @@ export const TIMEOUT_ACTION_OPTIONS = [ { value: 'skip', label: '跳过进入下一步' }, { value: 'transfer', label: '转人工' } ] + +export const SCRIPT_MODE_OPTIONS = [ + { value: 'fixed' as const, label: '固定话术', description: '话术内容固定不变' }, + { value: 'flexible' as const, label: '灵活话术', description: 'AI根据意图和上下文生成' }, + { value: 'template' as const, label: '模板话术', description: 'AI填充模板中的变量' } +] + +export const PRESET_CONSTRAINTS = [ + '必须礼貌', + '语气自然', + '简洁明了', + '不要生硬', + '不要重复' +] diff --git a/ai-service-admin/src/views/admin/decomposition-template/index.vue b/ai-service-admin/src/views/admin/decomposition-template/index.vue new file mode 100644 index 0000000..f53c4db --- /dev/null +++ b/ai-service-admin/src/views/admin/decomposition-template/index.vue @@ -0,0 +1,676 @@ + + + + + diff --git a/ai-service-admin/src/views/admin/intent-rule/index.vue b/ai-service-admin/src/views/admin/intent-rule/index.vue index c7309b2..717b243 100644 --- a/ai-service-admin/src/views/admin/intent-rule/index.vue +++ b/ai-service-admin/src/views/admin/intent-rule/index.vue @@ -4,7 +4,7 @@

意图规则管理

-

配置意图识别规则,让特定问题走固定回复或话术流程。

+

配置意图识别规则,让特定问题走固定回复或话术流程。[AC-IDSMETA-16]

@@ -48,6 +48,14 @@ + + +