# 元数据职责分层优化 - 技术设计 ## 1. 系统架构 ### 1.1 整体架构 ``` ┌─────────────────────────────────────────────────────────────────────────┐ │ 管理端 (ai-service-admin) │ ├─────────────────────────────────────────────────────────────────────────┤ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │ │ │ 元数据字段配置 │ │ 槽位定义配置 │ │ 按角色过滤视图 │ │ │ │ (field_roles) │ │ (SlotDefinition) │ │ (RoleFilter) │ │ │ └────────┬────────┘ └────────┬────────┘ └────────┬────────┘ │ └───────────┼────────────────────┼────────────────────┼───────────────────┘ │ │ │ ▼ ▼ ▼ ┌─────────────────────────────────────────────────────────────────────────┐ │ 后端服务 (ai-service) │ ├─────────────────────────────────────────────────────────────────────────┤ │ │ │ ┌─────────────────────────────────────────────────────────────────┐ │ │ │ API Layer │ │ │ │ ┌──────────────────────┐ ┌──────────────────────┐ │ │ │ │ │ MetadataFieldAPI │ │ SlotDefinitionAPI │ │ │ │ │ │ - CRUD │ │ - CRUD │ │ │ │ │ │ - getByRole │ │ - getByRole │ │ │ │ │ └──────────┬───────────┘ └──────────┬───────────┘ │ │ │ └─────────────┼────────────────────────┼──────────────────────────┘ │ │ │ │ │ │ ┌─────────────┼────────────────────────┼──────────────────────────┐ │ │ │ │ Service Layer │ │ │ │ │ ┌──────────▼───────────┐ ┌────────▼──────────┐ │ │ │ │ │ MetadataFieldService │ │ SlotDefinitionSvc │ │ │ │ │ │ - create/update │ │ - create/update │ │ │ │ │ │ - getByRole │ │ - getByRole │ │ │ │ │ │ - validateRoles │ │ - linkToField │ │ │ │ │ └──────────┬───────────┘ └────────┬──────────┘ │ │ │ └─────────────┼────────────────────────┼──────────────────────────┘ │ │ │ │ │ │ ┌─────────────┼────────────────────────┼──────────────────────────┐ │ │ │ │ Tool Integration │ │ │ │ │ ┌──────────▼────────────────────────▼──────────┐ │ │ │ │ │ RoleBasedFieldProvider │ │ │ │ │ │ - getFieldsByRole(role) │ │ │ │ │ │ - getSlotDefinitionsByRole(role) │ │ │ │ │ └──────────────────────┬───────────────────────┘ │ │ │ └─────────────────────────┼──────────────────────────────────────┘ │ │ │ │ │ ┌─────────────────────────┼──────────────────────────────────────┐ │ │ │ Tool Consumers │ │ │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │ │ │ │kb_search_ │ │memory_recall │ │intent_hint/ │ │ │ │ │ │dynamic │ │ │ │high_risk_ │ │ │ │ │ │[resource_ │ │[slot] │ │check │ │ │ │ │ │filter] │ │ │ │[routing_ │ │ │ │ │ └──────────────┘ └──────────────┘ │signal] │ │ │ │ │ └──────────────┘ │ │ │ │ ┌──────────────┐ │ │ │ │ │template_ │ │ │ │ │ │engine │ │ │ │ │ │[prompt_var] │ │ │ │ │ └──────────────┘ │ │ │ └────────────────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────┘ │ │ │ ▼ ▼ ▼ ┌─────────────────────────────────────────────────────────────────────────┐ │ 数据层 │ ├─────────────────────────────────────────────────────────────────────────┤ │ ┌─────────────────────┐ ┌─────────────────────┐ │ │ │ metadata_field_ │ │ slot_definitions │ │ │ │ definitions │ │ │ │ │ │ - id │ │ - id │ │ │ │ - field_key │ │ - slot_key │ │ │ │ - field_roles[] │ │ - type │ │ │ │ - ... │ │ - linked_field_id │ │ │ └─────────────────────┘ └─────────────────────┘ │ │ │ │ ┌─────────────────────┐ ┌─────────────────────┐ │ │ │ Redis Cache │ │ PostgreSQL │ │ │ │ - field_roles:by_tenant │ - 持久化存储 │ │ │ └─────────────────────┘ └─────────────────────┘ │ └─────────────────────────────────────────────────────────────────────────┘ ``` ### 1.2 模块依赖关系 ``` metadata-role-separation │ ├── metadata-governance (依赖) │ └── 元数据字段定义基础能力 │ ├── intent-driven-mid-platform (依赖) │ └── 中台运行时工具链 │ └── ai-service-admin (依赖) └── 管理端配置界面 ``` --- ## 2. 数据模型设计 ### 2.1 MetadataFieldDefinition 扩展 在现有 `metadata_field_definitions` 表基础上新增字段: ```sql -- 新增字段:field_roles ALTER TABLE metadata_field_definitions ADD COLUMN field_roles JSONB DEFAULT '[]'::jsonb; -- 创建 GIN 索引支持按角色查询 CREATE INDEX idx_metadata_field_definitions_roles ON metadata_field_definitions USING GIN (field_roles); -- 注释 COMMENT ON COLUMN metadata_field_definitions.field_roles IS '字段角色列表:resource_filter, slot, prompt_var, routing_signal'; ``` **字段定义**: | 字段名 | 类型 | 必填 | 默认值 | 说明 | |-------|------|-----|-------|------| | `field_roles` | JSONB | 否 | `[]` | 字段角色列表,存储字符串数组 | **角色枚举值**: ```python class FieldRole(str, Enum): RESOURCE_FILTER = "resource_filter" # 资源过滤 SLOT = "slot" # 运行时槽位 PROMPT_VAR = "prompt_var" # 提示词变量 ROUTING_SIGNAL = "routing_signal" # 路由信号 ``` ### 2.2 SlotDefinition 新增表 ```sql -- 槽位定义表 CREATE TABLE slot_definitions ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), tenant_id UUID NOT NULL, slot_key VARCHAR(100) NOT NULL, type VARCHAR(20) NOT NULL, required BOOLEAN NOT NULL DEFAULT FALSE, extract_strategy VARCHAR(20), validation_rule TEXT, ask_back_prompt TEXT, default_value JSONB, linked_field_id UUID REFERENCES metadata_field_definitions(id), created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), CONSTRAINT uk_slot_definitions_tenant_key UNIQUE (tenant_id, slot_key), CONSTRAINT chk_slot_definitions_type CHECK (type IN ('string', 'number', 'boolean', 'enum', 'array_enum')), CONSTRAINT chk_slot_definitions_extract_strategy CHECK (extract_strategy IS NULL OR extract_strategy IN ('rule', 'llm', 'user_input')) ); -- 索引 CREATE INDEX idx_slot_definitions_tenant ON slot_definitions(tenant_id); CREATE INDEX idx_slot_definitions_linked_field ON slot_definitions(linked_field_id); -- 注释 COMMENT ON TABLE slot_definitions IS '槽位定义表'; COMMENT ON COLUMN slot_definitions.slot_key IS '槽位键名,可与元数据字段 field_key 关联'; COMMENT ON COLUMN slot_definitions.linked_field_id IS '关联的元数据字段 ID'; ``` ### 2.3 ER 图 ``` ┌─────────────────────────────┐ ┌─────────────────────────────┐ │ metadata_field_definitions │ │ slot_definitions │ ├─────────────────────────────┤ ├─────────────────────────────┤ │ id (PK) │◄──────│ linked_field_id (FK) │ │ tenant_id │ │ id (PK) │ │ field_key │ │ tenant_id │ │ label │ │ slot_key │ │ type │ │ type │ │ required │ │ required │ │ options │ │ extract_strategy │ │ default_value │ │ validation_rule │ │ scope │ │ ask_back_prompt │ │ is_filterable │ │ default_value │ │ is_rank_feature │ │ created_at │ │ status │ │ updated_at │ │ field_roles ◀── NEW │ │ │ │ version │ │ │ │ created_at │ │ │ │ updated_at │ │ │ └─────────────────────────────┘ └─────────────────────────────┘ ``` --- ## 3. 核心流程设计 ### 3.1 字段职责配置流程 ``` ┌─────────────────────────────────────────────────────────────────────────┐ │ 管理端配置流程 │ ├─────────────────────────────────────────────────────────────────────────┤ │ │ │ 1. 管理员进入元数据字段配置页面 │ │ │ │ │ ▼ │ │ 2. 创建/编辑字段定义 │ │ │ │ │ ├── 填写基本信息 (field_key, label, type, etc.) │ │ │ │ │ ├── 选择字段角色 (field_roles 多选) │ │ │ ├── [ ] resource_filter - 资源过滤 │ │ │ ├── [ ] slot - 运行时槽位 │ │ │ ├── [ ] prompt_var - 提示词变量 │ │ │ └── [ ] routing_signal - 路由信号 │ │ │ │ │ ▼ │ │ 3. 后端校验 │ │ │ │ │ ├── field_key 格式校验 (小写字母数字下划线) │ │ ├── field_roles 枚举值校验 │ │ └── 租户内唯一性校验 │ │ │ │ │ ▼ │ │ 4. 保存到数据库 │ │ │ │ │ └── field_roles 以 JSONB 格式存储 │ │ │ └─────────────────────────────────────────────────────────────────────────┘ ``` ### 3.2 按角色查询流程 ``` ┌─────────────────────────────────────────────────────────────────────────┐ │ 按角色查询流程 │ ├─────────────────────────────────────────────────────────────────────────┤ │ │ │ 工具/模块请求字段 │ │ │ │ │ ▼ │ │ RoleBasedFieldProvider.getFieldsByRole(role, tenant_id) │ │ │ │ │ ▼ │ │ ┌─────────────────────────────────────────────────────────────────┐ │ │ │ SELECT * FROM metadata_field_definitions │ │ │ │ WHERE tenant_id = :tenant_id │ │ │ │ AND status = 'active' │ │ │ │ AND field_roles ? :role -- JSONB contains 查询 │ │ │ └─────────────────────────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ 返回字段定义列表 │ │ │ │ │ ▼ │ │ 工具按需消费字段 │ │ │ └─────────────────────────────────────────────────────────────────────────┘ ``` ### 3.3 工具协同改造流程 ``` ┌─────────────────────────────────────────────────────────────────────────┐ │ 工具协同改造流程 │ ├─────────────────────────────────────────────────────────────────────────┤ │ │ │ ┌──────────────────────────────────────────────────────────────────┐ │ │ │ kb_search_dynamic [AC-MRS-11] │ │ │ │ │ │ │ │ 改造前: │ │ │ │ filterable_fields = get_filterable_fields(tenant_id) │ │ │ │ WHERE is_filterable = true │ │ │ │ │ │ │ │ 改造后: │ │ │ │ filterable_fields = get_fields_by_role( │ │ │ │ tenant_id, role='resource_filter' │ │ │ │ ) │ │ │ └──────────────────────────────────────────────────────────────────┘ │ │ │ │ ┌──────────────────────────────────────────────────────────────────┐ │ │ │ memory_recall [AC-MRS-12] │ │ │ │ │ │ │ │ 改造前: │ │ │ │ slots = context.get('slots', {}) # 无角色过滤 │ │ │ │ │ │ │ │ 改造后: │ │ │ │ slot_fields = get_fields_by_role( │ │ │ │ tenant_id, role='slot' │ │ │ │ ) │ │ │ │ slots = {k: v for k, v in context.items() │ │ │ │ if k in slot_fields} │ │ │ └──────────────────────────────────────────────────────────────────┘ │ │ │ │ ┌──────────────────────────────────────────────────────────────────┐ │ │ │ intent_hint / high_risk_check [AC-MRS-13] │ │ │ │ │ │ │ │ 改造前: │ │ │ │ routing_fields = all_metadata_fields # 全量字段 │ │ │ │ │ │ │ │ 改造后: │ │ │ │ routing_fields = get_fields_by_role( │ │ │ │ tenant_id, role='routing_signal' │ │ │ │ ) │ │ │ └──────────────────────────────────────────────────────────────────┘ │ │ │ │ ┌──────────────────────────────────────────────────────────────────┐ │ │ │ template_engine [AC-MRS-14] │ │ │ │ │ │ │ │ 改造前: │ │ │ │ variables = extract_all_variables(template) │ │ │ │ values = get_all_metadata_values(context) │ │ │ │ │ │ │ │ 改造后: │ │ │ │ prompt_var_fields = get_fields_by_role( │ │ │ │ tenant_id, role='prompt_var' │ │ │ │ ) │ │ │ │ values = {k: v for k, v in context.items() │ │ │ │ if k in prompt_var_fields} │ │ │ └──────────────────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────┘ ``` --- ## 4. 接口设计 ### 4.1 后端 API 接口 | 接口 | 方法 | 说明 | AC | |------|------|------|-----| | `/admin/metadata-schemas` | GET | 获取字段列表(支持 role 过滤) | AC-MRS-06 | | `/admin/metadata-schemas` | POST | 创建字段(含 field_roles) | AC-MRS-01,02,03 | | `/admin/metadata-schemas/{id}` | PUT | 更新字段(含 field_roles) | AC-MRS-01 | | `/admin/metadata-schemas/{id}` | DELETE | 删除字段(无需兼容) | AC-MRS-16 | | `/admin/metadata-schemas/by-role` | GET | 按角色查询字段 | AC-MRS-04,05 | | `/admin/slot-definitions` | GET | 获取槽位定义列表 | - | | `/admin/slot-definitions` | POST | 创建槽位定义 | AC-MRS-07,08 | | `/admin/slot-definitions/{id}` | PUT | 更新槽位定义 | - | | `/admin/slot-definitions/{id}` | DELETE | 删除槽位定义 | AC-MRS-16 | | `/mid/slots/by-role` | GET | 运行时按角色获取槽位 | AC-MRS-10 | | `/mid/slots/{slot_key}` | GET | 获取运行时槽位值 | AC-MRS-09 | ### 4.2 内部服务接口 ```python class RoleBasedFieldProvider: """基于角色的字段提供者""" async def get_fields_by_role( self, tenant_id: str, role: FieldRole, include_deprecated: bool = False, ) -> list[MetadataFieldDefinition]: """ 按角色获取字段定义 Args: tenant_id: 租户ID role: 字段角色 include_deprecated: 是否包含已废弃字段 Returns: 字段定义列表 """ async def get_slot_definitions_by_role( self, tenant_id: str, role: FieldRole, ) -> list[SlotDefinition]: """ 按角色获取槽位定义(包含关联字段信息) """ ``` --- ## 5. 前端设计 ### 5.1 元数据字段配置页面改造 **新增组件**:`FieldRolesSelector.vue` ```vue ``` ### 5.2 按角色过滤视图 **新增过滤器**:在元数据字段列表页面增加角色过滤下拉框 ```vue ``` --- ## 6. 缓存策略 ### 6.1 缓存设计 ```python class FieldRoleCache: """字段角色缓存""" CACHE_KEY_PREFIX = "field_roles" CACHE_TTL = 300 # 5分钟 async def get_fields_by_role( self, tenant_id: str, role: FieldRole, ) -> list[MetadataFieldDefinition]: cache_key = f"{self.CACHE_KEY_PREFIX}:{tenant_id}:{role}" # 尝试从缓存获取 cached = await self._redis.get(cache_key) if cached: return self._deserialize(cached) # 从数据库查询 fields = await self._db_query(tenant_id, role) # 写入缓存 await self._redis.setex( cache_key, self.CACHE_TTL, self._serialize(fields), ) return fields async def invalidate(self, tenant_id: str): """失效租户所有角色缓存""" pattern = f"{self.CACHE_KEY_PREFIX}:{tenant_id}:*" keys = await self._redis.keys(pattern) if keys: await self._redis.delete(*keys) ``` ### 6.2 缓存失效策略 - 字段创建/更新/删除时失效该租户所有角色缓存 - 槽位定义变更时失效相关角色缓存 --- ## 7. 异常处理 ### 7.1 错误码定义 | 错误码 | 说明 | HTTP 状态码 | |-------|------|------------| | `INVALID_ROLE` | 无效的角色参数 | 400 | | `FIELD_KEY_EXISTS` | field_key 已存在 | 409 | | `SLOT_KEY_EXISTS` | slot_key 已存在 | 409 | | `LINKED_FIELD_NOT_FOUND` | 关联字段不存在 | 404 | ### 7.2 异常处理示例 ```python class InvalidRoleError(Exception): """无效角色异常""" def __init__(self, role: str): self.role = role self.valid_roles = [r.value for r in FieldRole] super().__init__( f"Invalid role '{role}'. Valid roles are: {', '.join(self.valid_roles)}" ) ``` --- ## 8. 测试策略 ### 8.1 单元测试 - `RoleBasedFieldProvider` 按角色查询测试 - `FieldRoleCache` 缓存读写测试 - 字段角色校验测试 ### 8.2 集成测试 - API 端点测试(CRUD + 按角色查询) - 工具协同改造测试(验证各工具只消费对应角色字段) ### 8.3 契约测试 - OpenAPI Schema 校验 - 响应结构验证 --- ## 9. 迁移与部署 ### 9.1 数据库迁移 ```sql -- 1. 新增 field_roles 字段 ALTER TABLE metadata_field_definitions ADD COLUMN field_roles JSONB DEFAULT '[]'::jsonb; -- 2. 创建索引 CREATE INDEX idx_metadata_field_definitions_roles ON metadata_field_definitions USING GIN (field_roles); -- 3. 创建 slot_definitions 表 CREATE TABLE slot_definitions ( -- ... 见 2.2 节 ); -- 4. 初始化现有字段的默认角色(可选) -- 根据 is_filterable 推断 resource_filter 角色 UPDATE metadata_field_definitions SET field_roles = '["resource_filter"]'::jsonb WHERE is_filterable = true AND status = 'active'; ``` ### 9.2 部署顺序 1. 执行数据库迁移脚本 2. 部署后端服务(向后兼容,field_roles 可为空) 3. 部署前端页面 4. 配置字段角色 --- ## 10. 监控与观测 ### 10.1 关键指标 | 指标名 | 说明 | |-------|------| | `field_role_query_count` | 按角色查询次数 | | `field_role_query_latency` | 按角色查询延迟 | | `field_role_cache_hit_rate` | 角色缓存命中率 | | `tool_field_consumption` | 工具字段消费统计 | ### 10.2 日志字段 ```json { "event": "field_role_query", "tenant_id": "xxx", "role": "resource_filter", "field_count": 5, "duration_ms": 12, "cache_hit": true } ```