diff --git a/README.md b/README.md index ef5e249..d7ab28a 100644 --- a/README.md +++ b/README.md @@ -75,14 +75,13 @@ | 库 | 文件 | 内容 | |---|---|---| | business | `data/business.db` | 数据集、文档、分块、向量(vec0)、全文索引(FTS5)、解析任务、知识图谱、合同标注 | -| system | `data/system.db` | 全局设置(app_config)与模型配置 | +| system | `data/system.db` | 模型配置 | | chat | `data/chat.db` | 会话与消息 | -表清单(13 张实体表 + 2 张虚拟表): +表清单(12 张实体表 + 2 张虚拟表): | 表名 | 库 | 类型 | 说明 | |---|---|---|---| -| `app_config` | system | 实体 | 全局键值配置(分块默认参数) | | `model_config` | system | 实体 | 模型配置(chat / embedding,is_default 标记默认) | | `kb_dataset` | business | 实体 | 数据集(绑定向量模型、分块参数) | | `kb_document` | business | 实体 | 文档(6 态状态机) | @@ -202,6 +201,7 @@ ollama pull nomic-embed-text | `vector.dim` | 向量维度,须与向量模型一致 | | `database.cache.ttl` | DAO 查询缓存秒数 | | `chat.timeout` / `chat.max_retries` | 对话模型 API 超时(秒)/ 失败重试次数 | +| `chunk.default_size` / `chunk.default_overlap` / `react.default_rounds` | 数据集分块大小/重叠/智能体轮次默认值(新建数据集传 0/-1 时使用) | | `pool.*` | 各并行点协程池并发度(缺失或非法回退代码内默认值):`kg_extract`(4) 逐 chunk 图谱抽取、`annotation_clause`(4) 合同逐条款标注、`annotation_dataset`(8) 条款内逐数据集召回、`chat`(4) 问答检索与图增强并行、`chat_retrieve`(4) 检索向量与全文并行 | ## API 概览 @@ -210,7 +210,7 @@ ollama pull nomic-embed-text | 分组 | 接口 | |---|---| -| `/system-config` | `POST /login`(访问令牌登录)、`GET /settings`、`POST /save-settings`(分块默认值) | +| `/system-config` | `POST /login`(访问令牌登录) | | `/model-config` | `GET /list`、`POST /save`、`POST /delete`、`POST /set-default`、`POST /test`(连通性测试) | | `/dataset` | `GET /list`、`POST /save`(新建/编辑)、`POST /delete`(有文档拒绝) | | `/document` | `POST /upload`(multipart)、`GET /list`、`GET /detail`、`POST /delete`、`POST /reembed`(仅重算向量) | @@ -226,12 +226,12 @@ ollama pull nomic-embed-text | 页面 | 功能 | |---|---| | 登录页 | 输入访问令牌登录,无注册入口 | -| 数据集列表 | 新建/编辑/删除(绑定向量模型必选、分块参数带出全局默认);变更模型/分块 → 确认弹窗提示重新处理 | +| 数据集列表 | 新建/编辑/删除(绑定向量模型必选、分块参数传 0/-1 使用 config.yml 全局默认);变更模型/分块 → 确认弹窗提示重新处理 | | 数据集详情 | 文档上传(拖拽)、6 态解析状态/分块数/失败重试/「图谱未构建」提示、分块预览与编辑 | | 图谱页 | 实体/关系列表 + eCharts 力导向图 | | 合同标注页 | 选语料数据集 → 上传合同 → 任务进度(3s 轮询)→ 条款-标注对照抽屉 → 导出标注版 HTML | | 问答页 | 会话列表 + 知识库下拉,SSE 流式渲染,引用折叠面板 | -| 设置页 | 分块默认值 + 模型配置 CRUD(chat/embedding 两个 tab)、连通性测试、设置默认 | +| 设置页 | 模型配置 CRUD(chat/embedding 两个 tab)、连通性测试、设置默认 | ## 项目结构 diff --git a/config.yml b/config.yml index 0d18bb5..05ea060 100644 --- a/config.yml +++ b/config.yml @@ -29,6 +29,13 @@ chat: timeout: 600 # 对话模型API请求超时时间(秒) max_retries: 3 # 请求失败最大重试次数 +# 数据集默认值(新建数据集传 0/-1 时使用,数据集内可覆盖) +chunk: + default_size: 800 # 分块最大字数 + default_overlap: 150 # 分块重叠字数 +react: + default_rounds: 0 # 智能体轮次(0=关闭) + # 协程池配置(各并行点并发度,缺失或 <1 时回退代码内默认值) pool: kg_extract: 4 # 知识图谱:逐 chunk LLM 抽取 diff --git a/data/business.db b/data/business.db index 460faca..53b6bd5 100644 Binary files a/data/business.db and b/data/business.db differ diff --git a/data/chat.db b/data/chat.db index 30c4cc0..a277aca 100644 Binary files a/data/chat.db and b/data/chat.db differ diff --git a/data/system.db b/data/system.db index d914927..83a9ed4 100644 Binary files a/data/system.db and b/data/system.db differ diff --git a/kb/consts/consts.go b/kb/consts/consts.go index 35f4d61..9978f64 100644 --- a/kb/consts/consts.go +++ b/kb/consts/consts.go @@ -26,11 +26,6 @@ const ( MaxReactRounds = 10 // ReAct 轮次上限 ToolResultMaxChars = 1500 // search 工具返回的单条 chunk 截断字数(控制上下文体积) - // 全局设置键(app_config 表) - SettingsKeyChunkSize = "chunk_default_size" - SettingsKeyChunkOverlap = "chunk_default_overlap" - SettingsKeyReactRounds = "react_default_rounds" - ParsePollIntervalSeconds = 5 // 解析/标注任务轮询间隔 EmbedBatchSize = 16 // 单次向量化请求的文本批量 diff --git a/kb/consts/table_name.go b/kb/consts/table_name.go index 4e74179..62ba6ab 100644 --- a/kb/consts/table_name.go +++ b/kb/consts/table_name.go @@ -2,7 +2,6 @@ package consts const ( TableNameModelConfig = "model_config" - TableNameAppConfig = "app_config" TableNameDataset = "kb_dataset" TableNameDocument = "kb_document" TableNameChunk = "kb_chunk" diff --git a/kb/controller/system_config_controller.go b/kb/controller/system_config_controller.go index 178d18d..b6550e6 100644 --- a/kb/controller/system_config_controller.go +++ b/kb/controller/system_config_controller.go @@ -18,18 +18,3 @@ func (c *systemConfig) Login(ctx context.Context, req *dto.LoginReq) (res *dto.L } return &dto.LoginRes{Token: token}, nil } - -func (c *systemConfig) GetSettings(ctx context.Context, _ *dto.GetSettingsReq) (*dto.GetSettingsRes, error) { - size, overlap, rounds, err := service.SystemConfigService.GetSettings(ctx) - if err != nil { - return nil, err - } - return &dto.GetSettingsRes{ChunkSize: size, ChunkOverlap: overlap, ReactRounds: rounds}, nil -} - -func (c *systemConfig) SaveSettings(ctx context.Context, req *dto.SaveSettingsReq) (*dto.SaveSettingsRes, error) { - if err := service.SystemConfigService.SaveSettings(ctx, req.ChunkSize, req.ChunkOverlap, req.ReactRounds); err != nil { - return nil, err - } - return &dto.SaveSettingsRes{}, nil -} diff --git a/kb/dao/app_config_dao.go b/kb/dao/app_config_dao.go deleted file mode 100644 index cb658ef..0000000 --- a/kb/dao/app_config_dao.go +++ /dev/null @@ -1,44 +0,0 @@ -package dao - -import ( - "context" - - "rag-local/kb/consts" - - "github.com/gogf/gf/v2/frame/g" -) - -var AppConfig = &appConfigDao{} - -type appConfigDao struct{} - -func init() { - ctx := context.Background() - _, err := g.DB(consts.DbGroupSystem).Exec(ctx, `CREATE TABLE IF NOT EXISTS `+consts.TableNameAppConfig+` ( - cfg_key TEXT PRIMARY KEY, - cfg_value TEXT NOT NULL DEFAULT '', - updated_at DATETIME DEFAULT (datetime('now','localtime')) - )`) - if err != nil { - g.Log().Warningf(ctx, "create app_config table failed: %v", err) - } -} - -// GetInt 读取全局设置值,未设置或非法时返回默认值 def -func (d *appConfigDao) GetInt(ctx context.Context, key string, def int) int { - r, err := g.DB(consts.DbGroupSystem).Ctx(ctx).GetValue(ctx, - "SELECT cfg_value FROM "+consts.TableNameAppConfig+" WHERE cfg_key=?", key) - if err != nil || r.IsEmpty() { - return def - } - return r.Int() -} - -// SetInt 写入全局设置值(UPSERT) -func (d *appConfigDao) SetInt(ctx context.Context, key string, v int) error { - _, err := g.DB(consts.DbGroupSystem).Ctx(ctx).Exec(ctx, - "INSERT INTO "+consts.TableNameAppConfig+"(cfg_key, cfg_value) VALUES(?, ?) "+ - "ON CONFLICT(cfg_key) DO UPDATE SET cfg_value=excluded.cfg_value, updated_at=datetime('now','localtime')", - key, v) - return err -} diff --git a/kb/model/dto/system_config_dto.go b/kb/model/dto/system_config_dto.go index c7fc049..44b74d7 100644 --- a/kb/model/dto/system_config_dto.go +++ b/kb/model/dto/system_config_dto.go @@ -10,22 +10,3 @@ type LoginReq struct { type LoginRes struct { Token string `json:"token"` } - -type GetSettingsReq struct { - g.Meta `path:"/settings" method:"get" tags:"系统配置" summary:"获取全局设置"` -} - -type GetSettingsRes struct { - ChunkSize int `json:"chunk_size"` // 默认分块大小 - ChunkOverlap int `json:"chunk_overlap"` // 默认重叠字数 - ReactRounds int `json:"react_rounds"` // 默认智能体轮次(0=关闭) -} - -type SaveSettingsReq struct { - g.Meta `path:"/save-settings" method:"post" tags:"系统配置" summary:"保存全局设置"` - ChunkSize int `json:"chunk_size"` - ChunkOverlap int `json:"chunk_overlap"` - ReactRounds int `json:"react_rounds"` -} - -type SaveSettingsRes struct{} diff --git a/kb/service/dataset_service.go b/kb/service/dataset_service.go index a4fb8c1..e46193c 100644 --- a/kb/service/dataset_service.go +++ b/kb/service/dataset_service.go @@ -34,6 +34,16 @@ func (s *datasetService) Save(ctx context.Context, m *entity.Dataset) (int64, er return 0, gerror.Newf("召回数量需在 -1~%d 之间(-1=尽量多,0=全局默认)", consts.MaxRecallTopK) } } + // 0/-1 = 使用 config.yml 全局默认,落库前回填为实际值(Insert/Update 一致) + if m.ChunkSize <= 0 { + m.ChunkSize = g.Cfg().MustGet(ctx, "chunk.default_size", consts.DefaultChunkSize).Int() + } + if m.ChunkOverlap < 0 { + m.ChunkOverlap = g.Cfg().MustGet(ctx, "chunk.default_overlap", consts.DefaultChunkOverlap).Int() + } + if m.ReactRounds < 0 { + m.ReactRounds = g.Cfg().MustGet(ctx, "react.default_rounds", 0).Int() + } if m.Id > 0 { old, err := dao.Dataset.GetOne(ctx, m.Id) if err != nil { @@ -56,12 +66,6 @@ func (s *datasetService) Save(ctx context.Context, m *entity.Dataset) (int64, er } return m.Id, nil } - if m.ChunkSize <= 0 { - m.ChunkSize = consts.DefaultChunkSize - } - if m.ChunkOverlap < 0 { - m.ChunkOverlap = consts.DefaultChunkOverlap - } return dao.Dataset.Insert(ctx, m) } diff --git a/kb/service/system_config_service.go b/kb/service/system_config_service.go index 3c0b35c..b9e3dcc 100644 --- a/kb/service/system_config_service.go +++ b/kb/service/system_config_service.go @@ -4,8 +4,6 @@ import ( "context" "rag-local/common" - "rag-local/kb/consts" - "rag-local/kb/dao" "github.com/gogf/gf/v2/errors/gerror" ) @@ -27,30 +25,3 @@ func (s *systemConfigService) Login(ctx context.Context, token string) (string, } return common.SignToken("owner", common.AccessTokenFingerprint(), common.TokenExpireSeconds) } - -// GetSettings 读取全局分块默认值与智能体轮次默认值(未设置时用内置默认值) -func (s *systemConfigService) GetSettings(ctx context.Context) (chunkSize, chunkOverlap, reactRounds int, err error) { - return dao.AppConfig.GetInt(ctx, consts.SettingsKeyChunkSize, consts.DefaultChunkSize), - dao.AppConfig.GetInt(ctx, consts.SettingsKeyChunkOverlap, consts.DefaultChunkOverlap), - dao.AppConfig.GetInt(ctx, consts.SettingsKeyReactRounds, 0), nil -} - -// SaveSettings 保存全局分块默认值与智能体轮次默认值 -func (s *systemConfigService) SaveSettings(ctx context.Context, chunkSize, chunkOverlap, reactRounds int) error { - if chunkSize < 50 || chunkSize > 5000 { - return gerror.New("分块大小需在 50~5000 之间") - } - if chunkOverlap < 0 || chunkOverlap > 500 { - return gerror.New("重叠字数需在 0~500 之间") - } - if reactRounds < 0 || reactRounds > consts.MaxReactRounds { - return gerror.Newf("智能体轮次需在 0~%d 之间(0=关闭)", consts.MaxReactRounds) - } - if err := dao.AppConfig.SetInt(ctx, consts.SettingsKeyChunkSize, chunkSize); err != nil { - return err - } - if err := dao.AppConfig.SetInt(ctx, consts.SettingsKeyChunkOverlap, chunkOverlap); err != nil { - return err - } - return dao.AppConfig.SetInt(ctx, consts.SettingsKeyReactRounds, reactRounds) -} diff --git a/ui-src/src/api/settings.js b/ui-src/src/api/settings.js deleted file mode 100644 index 695523d..0000000 --- a/ui-src/src/api/settings.js +++ /dev/null @@ -1,9 +0,0 @@ -import request from './request.js' - -export function getSettings() { - return request.get('/system-config/settings') -} - -export function saveSettings(data) { - return request.post('/system-config/save-settings', data) -} diff --git a/ui-src/src/views/DatasetList.vue b/ui-src/src/views/DatasetList.vue index 97ac4b9..de39126 100644 --- a/ui-src/src/views/DatasetList.vue +++ b/ui-src/src/views/DatasetList.vue @@ -49,22 +49,22 @@ - -
每块最大字数。系统自动组合分块策略:优先识别文档结构(条文/章节/编号等)按结构切分,无结构时依次按标题感知、语义、递归切分
+ +
每块最大字数,0=使用 config.yml 全局默认。系统自动组合分块策略:优先识别文档结构(条文/章节/编号等)按结构切分,无结构时依次按标题感知、语义、递归切分
- -
相邻分块间的重叠字数,用于保持上下文连贯(语义切分路径自动忽略)
+ +
相邻分块间的重叠字数,-1=使用 config.yml 全局默认,0=不重叠(语义切分路径自动忽略)
- -
0=关闭(单次检索问答);1~10=启用 ReAct 智能体模式,模型可自主多次调用检索工具,此为轮次上限
+ +
-1=使用 config.yml 全局默认;0=关闭(单次检索问答);1~10=启用 ReAct 智能体模式,模型可自主多次调用检索工具,此为轮次上限
@@ -106,7 +106,6 @@ import { onMounted, ref } from 'vue' import { ElMessage, ElMessageBox } from 'element-plus' import { listDatasets, saveDataset, deleteDataset } from '../api/dataset.js' import { listModelConfigs } from '../api/model_config.js' -import { getSettings } from '../api/settings.js' // 把识别出的结构正则转成可读名称(第[一二三四五六七八九十百千]+条 → 条文) function humanizePattern(p) { @@ -123,25 +122,16 @@ function humanizePattern(p) { const datasets = ref([]) const embedders = ref([]) -const defaults = ref({ chunk_size: 800, chunk_overlap: 150, react_rounds: 0, vec_top_k: 0, fts_top_k: 0, rerank_top_k: 0, recall_top_k: 0 }) const loading = ref(false) const saving = ref(false) const dialogVisible = ref(false) const editing = ref(false) let editingRowCfgId = 0 let editingRowChunk = { chunk_size: 0, chunk_overlap: 0 } -const form = ref({ id: 0, name: '', description: '', embedding_cfg_id: 0, chunk_size: 800, chunk_overlap: 150, react_rounds: 0, vec_top_k: 0, fts_top_k: 0, rerank_top_k: 0, recall_top_k: 0 }) +const form = ref({ id: 0, name: '', description: '', embedding_cfg_id: 0, chunk_size: 0, chunk_overlap: -1, react_rounds: -1, vec_top_k: 0, fts_top_k: 0, rerank_top_k: 0, recall_top_k: 0 }) onMounted(async () => { await load() - try { - const s = await getSettings() - if (s) { - defaults.value.chunk_size = s.chunk_size || 800 - defaults.value.chunk_overlap = s.chunk_overlap ?? 150 - defaults.value.react_rounds = s.react_rounds ?? 0 - } - } catch { /* 忽略 */ } try { const m = await listModelConfigs('embedding') if (m && m.list) embedders.value = m.list @@ -167,9 +157,9 @@ function openCreate() { editing.value = false editingRowCfgId = 0 editingRowChunk = { chunk_size: 0, chunk_overlap: 0 } - // 默认选中默认向量模型,其次第一个;分块大小/重叠/智能体轮次带出全局设置值 + // 默认选中默认向量模型,其次第一个;分块大小/重叠/智能体轮次传 0/-1 使用 config.yml 全局默认 const def = embedders.value.find(x => x.is_default === 1) || embedders.value[0] - form.value = { id: 0, name: '', description: '', embedding_cfg_id: def?.id || 0, chunk_size: defaults.value.chunk_size, chunk_overlap: defaults.value.chunk_overlap, react_rounds: defaults.value.react_rounds, vec_top_k: defaults.value.vec_top_k, fts_top_k: defaults.value.fts_top_k, rerank_top_k: defaults.value.rerank_top_k, recall_top_k: defaults.value.recall_top_k } + form.value = { id: 0, name: '', description: '', embedding_cfg_id: def?.id || 0, chunk_size: 0, chunk_overlap: -1, react_rounds: -1, vec_top_k: 0, fts_top_k: 0, rerank_top_k: 0, recall_top_k: 0 } dialogVisible.value = true } diff --git a/ui-src/src/views/Settings.vue b/ui-src/src/views/Settings.vue index df9f1c9..74dce79 100644 --- a/ui-src/src/views/Settings.vue +++ b/ui-src/src/views/Settings.vue @@ -1,20 +1,5 @@