616 lines
34 KiB
Markdown
616 lines
34 KiB
Markdown
|
|
# 元数据职责分层优化 - 技术设计
|
|||
|
|
|
|||
|
|
## 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
|
|||
|
|
<template>
|
|||
|
|
<div class="field-roles-selector">
|
|||
|
|
<label>字段角色</label>
|
|||
|
|
<el-checkbox-group v-model="selectedRoles">
|
|||
|
|
<el-checkbox
|
|||
|
|
v-for="role in availableRoles"
|
|||
|
|
:key="role.value"
|
|||
|
|
:label="role.value"
|
|||
|
|
>
|
|||
|
|
<span>{{ role.label }}</span>
|
|||
|
|
<el-tooltip :content="role.description">
|
|||
|
|
<el-icon><QuestionFilled /></el-icon>
|
|||
|
|
</el-tooltip>
|
|||
|
|
</el-checkbox>
|
|||
|
|
</el-checkbox-group>
|
|||
|
|
</div>
|
|||
|
|
</template>
|
|||
|
|
|
|||
|
|
<script setup>
|
|||
|
|
const availableRoles = [
|
|||
|
|
{
|
|||
|
|
value: 'resource_filter',
|
|||
|
|
label: '资源过滤',
|
|||
|
|
description: '用于 KB 文档检索时的元数据过滤'
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
value: 'slot',
|
|||
|
|
label: '运行时槽位',
|
|||
|
|
description: '对话流程中的结构化槽位,用于信息收集'
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
value: 'prompt_var',
|
|||
|
|
label: '提示词变量',
|
|||
|
|
description: '注入到 LLM Prompt 中的变量'
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
value: 'routing_signal',
|
|||
|
|
label: '路由信号',
|
|||
|
|
description: '用于意图路由和风险判断的信号'
|
|||
|
|
},
|
|||
|
|
];
|
|||
|
|
</script>
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 5.2 按角色过滤视图
|
|||
|
|
|
|||
|
|
**新增过滤器**:在元数据字段列表页面增加角色过滤下拉框
|
|||
|
|
|
|||
|
|
```vue
|
|||
|
|
<template>
|
|||
|
|
<div class="filter-bar">
|
|||
|
|
<el-select
|
|||
|
|
v-model="selectedRole"
|
|||
|
|
placeholder="按角色过滤"
|
|||
|
|
clearable
|
|||
|
|
@change="handleRoleFilter"
|
|||
|
|
>
|
|||
|
|
<el-option
|
|||
|
|
v-for="role in availableRoles"
|
|||
|
|
:key="role.value"
|
|||
|
|
:label="role.label"
|
|||
|
|
:value="role.value"
|
|||
|
|
/>
|
|||
|
|
</el-select>
|
|||
|
|
</div>
|
|||
|
|
</template>
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 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
|
|||
|
|
}
|
|||
|
|
```
|