Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
- Fix seed template tools to match actual runtime tool names (file_read/file_write/shell_exec/web_fetch) - Persist system_prompt/temperature/max_tokens via identity system in agentStore.createFromTemplate() - Fire-and-forget assignTemplate() in AgentOnboardingWizard - Fix saas-relay-client unused variable warning - Make pgvector extension optional in knowledge_base migration - Increase StreamBridge timeout from 30s to 90s for thinking models
137 lines
5.3 KiB
SQL
137 lines
5.3 KiB
SQL
-- Migration: Knowledge Base tables with optional pgvector support
|
||
-- 5 tables: knowledge_categories, knowledge_items, knowledge_chunks,
|
||
-- knowledge_versions, knowledge_usage
|
||
--
|
||
-- pgvector is optional: if the extension is not installed, the embedding
|
||
-- column and HNSW index are skipped. All other tables work normally.
|
||
|
||
-- 行业分类树
|
||
CREATE TABLE IF NOT EXISTS knowledge_categories (
|
||
id TEXT PRIMARY KEY,
|
||
name VARCHAR(100) NOT NULL,
|
||
description TEXT,
|
||
parent_id TEXT REFERENCES knowledge_categories(id) ON DELETE RESTRICT,
|
||
icon VARCHAR(50),
|
||
sort_order INT DEFAULT 0,
|
||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
||
CHECK (id != parent_id)
|
||
);
|
||
CREATE INDEX IF NOT EXISTS idx_kc_parent ON knowledge_categories(parent_id);
|
||
|
||
-- 知识条目
|
||
CREATE TABLE IF NOT EXISTS knowledge_items (
|
||
id TEXT PRIMARY KEY,
|
||
category_id TEXT NOT NULL REFERENCES knowledge_categories(id) ON DELETE RESTRICT,
|
||
title VARCHAR(255) NOT NULL,
|
||
content TEXT NOT NULL,
|
||
keywords TEXT[] DEFAULT '{}',
|
||
related_questions TEXT[] DEFAULT '{}',
|
||
priority INT DEFAULT 0,
|
||
status VARCHAR(20) DEFAULT 'active' CHECK (status IN ('active', 'archived', 'deprecated', 'draft')),
|
||
version INT DEFAULT 1,
|
||
source VARCHAR(50) DEFAULT 'manual',
|
||
tags TEXT[] DEFAULT '{}',
|
||
created_by TEXT NOT NULL REFERENCES accounts(id),
|
||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
||
CHECK (length(content) <= 100000)
|
||
);
|
||
CREATE INDEX IF NOT EXISTS idx_ki_category ON knowledge_items(category_id);
|
||
CREATE INDEX IF NOT EXISTS idx_ki_status_updated ON knowledge_items(status, updated_at DESC);
|
||
CREATE INDEX IF NOT EXISTS idx_ki_keywords ON knowledge_items USING GIN(keywords);
|
||
|
||
-- 知识分块(RAG 检索核心)
|
||
-- 基础表不含 embedding 列;若 pgvector 可用则后续通过 DO 块添加
|
||
CREATE TABLE IF NOT EXISTS knowledge_chunks (
|
||
id TEXT PRIMARY KEY,
|
||
item_id TEXT NOT NULL REFERENCES knowledge_items(id) ON DELETE CASCADE,
|
||
chunk_index INT NOT NULL,
|
||
content TEXT NOT NULL,
|
||
keywords TEXT[] DEFAULT '{}',
|
||
created_at TIMESTAMPTZ DEFAULT NOW()
|
||
);
|
||
CREATE UNIQUE INDEX IF NOT EXISTS idx_kchunks_item_idx ON knowledge_chunks(item_id, chunk_index);
|
||
CREATE INDEX IF NOT EXISTS idx_kchunks_item ON knowledge_chunks(item_id);
|
||
CREATE INDEX IF NOT EXISTS idx_kchunks_keywords ON knowledge_chunks USING GIN(keywords);
|
||
|
||
-- 条件添加 embedding 列和 HNSW 索引(仅当 pgvector 可用时)
|
||
DO $$ BEGIN
|
||
PERFORM set_config('client_min_messages', 'warning', true);
|
||
CREATE EXTENSION IF NOT EXISTS vector;
|
||
IF NOT EXISTS (
|
||
SELECT 1 FROM information_schema.columns
|
||
WHERE table_name = 'knowledge_chunks' AND column_name = 'embedding'
|
||
) THEN
|
||
ALTER TABLE knowledge_chunks ADD COLUMN embedding vector(1536);
|
||
END IF;
|
||
IF NOT EXISTS (SELECT 1 FROM pg_indexes WHERE indexname = 'idx_kchunks_embedding') THEN
|
||
CREATE INDEX idx_kchunks_embedding ON knowledge_chunks
|
||
USING hnsw (embedding vector_cosine_ops)
|
||
WITH (m = 16, ef_construction = 128);
|
||
END IF;
|
||
RAISE NOTICE 'pgvector enabled for knowledge base';
|
||
EXCEPTION WHEN OTHERS THEN
|
||
RAISE NOTICE 'pgvector not available, vector features disabled: %', SQLERRM;
|
||
END $$;
|
||
|
||
-- 版本快照
|
||
CREATE TABLE IF NOT EXISTS knowledge_versions (
|
||
id TEXT PRIMARY KEY,
|
||
item_id TEXT NOT NULL REFERENCES knowledge_items(id) ON DELETE CASCADE,
|
||
version INT NOT NULL,
|
||
title VARCHAR(255) NOT NULL,
|
||
content TEXT NOT NULL,
|
||
keywords TEXT[] DEFAULT '{}',
|
||
related_questions TEXT[] DEFAULT '{}',
|
||
change_summary TEXT,
|
||
created_by TEXT NOT NULL REFERENCES accounts(id),
|
||
created_at TIMESTAMPTZ DEFAULT NOW()
|
||
);
|
||
CREATE INDEX IF NOT EXISTS idx_kv_item ON knowledge_versions(item_id);
|
||
|
||
-- 使用追踪
|
||
CREATE TABLE IF NOT EXISTS knowledge_usage (
|
||
id TEXT PRIMARY KEY,
|
||
item_id TEXT REFERENCES knowledge_items(id) ON DELETE SET NULL,
|
||
chunk_id TEXT REFERENCES knowledge_chunks(id) ON DELETE SET NULL,
|
||
session_id VARCHAR(100),
|
||
query_text TEXT,
|
||
relevance_score FLOAT,
|
||
was_injected BOOLEAN DEFAULT FALSE,
|
||
agent_feedback VARCHAR(20) CHECK (agent_feedback IN ('positive', 'negative')),
|
||
created_at TIMESTAMPTZ DEFAULT NOW()
|
||
);
|
||
CREATE INDEX IF NOT EXISTS idx_ku_item ON knowledge_usage(item_id) WHERE item_id IS NOT NULL;
|
||
CREATE INDEX IF NOT EXISTS idx_ku_created_brin ON knowledge_usage USING brin(created_at);
|
||
|
||
-- 权限种子数据(使用 jsonb 操作避免 REPLACE 脆弱性)
|
||
UPDATE roles
|
||
SET permissions = (
|
||
SELECT '[' || string_agg('"' || elem || '"', ', ') || ']'
|
||
FROM (
|
||
SELECT DISTINCT elem
|
||
FROM json_array_elements_text(permissions::json) AS elem
|
||
UNION ALL SELECT 'knowledge:read'
|
||
UNION ALL SELECT 'knowledge:write'
|
||
UNION ALL SELECT 'knowledge:admin'
|
||
UNION ALL SELECT 'knowledge:search'
|
||
) sub
|
||
)
|
||
WHERE id = 'super_admin'
|
||
AND permissions NOT LIKE '%knowledge:read%';
|
||
|
||
UPDATE roles
|
||
SET permissions = (
|
||
SELECT '[' || string_agg('"' || elem || '"', ', ') || ']'
|
||
FROM (
|
||
SELECT DISTINCT elem
|
||
FROM json_array_elements_text(permissions::json) AS elem
|
||
UNION ALL SELECT 'knowledge:read'
|
||
UNION ALL SELECT 'knowledge:write'
|
||
UNION ALL SELECT 'knowledge:search'
|
||
) sub
|
||
)
|
||
WHERE id = 'admin'
|
||
AND permissions NOT LIKE '%knowledge:read%';
|