Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion ui/src/api/API_README.md
Original file line number Diff line number Diff line change
Expand Up @@ -510,9 +510,24 @@ putDocument 统一更新名称、启停及设置,不按字段重复封装;
| putBatchDeleteParagraphs | PUT batch_delete,`{ id_list }`,服务端清理关联与索引 |

标题最多 256,后端内容上限 102400;界面限制见 View 文档。
问题候选在 knowledge/problem.ts 的 getProblemsPage,支持 content 搜索,类型 ParagraphProblem。
问题候选在 knowledge/problem.ts 的 getProblemsPage,支持 content 搜索,分页记录类型为 ProblemItem。
Workspace 共享 Paragraph API 仅 getParagraphPage、getParagraphDetail,不补问题查询或维护。

### 知识库问题

四范围 `knowledge/problem.ts` 使用各自知识库前缀追加 `/<knowledgeId>/problem`,
由知识库及文档详情上下文的 `resourceApi.problem` 选择,不固定 Workspace。
`getProblemsPage(knowledgeId, page, query)` 返回 `ResponsePage<ProblemItem>`,query 仅传 content。
`ProblemItem` 包含 id、content、paragraph_count、hit_num、last_hit_time、create_time、update_time;
分段已有问题仍使用轻量 `ParagraphProblem`,不要求包含列表统计字段。

Workspace、System 资源管理、System 共享提供完整问题 API:
`postProblems` 直接提交内容字符串数组,`putProblem` 提交 `{ content }`,单项 DELETE 使用问题 ID,
`putBatchDeleteProblems` 直接提交问题 ID 数组;单个问题内容最多 256 字符。
`getProblemParagraphs` 返回 `ProblemParagraph[]`,包含分段字段及 knowledge_id、document_name。
取消关联复用 Paragraph API 的 `putUnassociateParagraphProblem`,不调用问题或分段删除接口。
Workspace 共享仅有 `getProblemsPage`,不补造详情或写接口,不回退其他范围。

### 标签与文档关联

四范围 tags.ts 按上述知识库前缀维护;Workspace 共享只 getKnowledgeTags,其余范围提供维护和关联。
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import { del, get, post, put } from '@/api/admin/core/request'
import type { ParamsPage, ResponsePage } from '@/api/admin/core/types'
import type { ParagraphProblem, ProblemBatchAssociationPayload, ProblemParagraph } from '@/api/types'
import type { ProblemItem, ProblemBatchAssociationPayload, ProblemParagraph } from '@/api/types'

const getPrefix = (knowledgeId: string) => `/system/resource/knowledge/${knowledgeId}/problem`

/** 批量创建知识库问题。 */
const postProblems = (knowledgeId: string, contents: string[]) => post<string[], void>(getPrefix(knowledgeId), contents)

/** 分页搜索知识库问题,供分段选择已有问题进行关联。 */
/** 分页搜索知识库问题,供问题列表及分段关联候选使用。 */
const getProblemsPage = (knowledgeId: string, page: ParamsPage, query?: { content?: string }) =>
get<ResponsePage<ParagraphProblem>>(`${getPrefix(knowledgeId)}/${page.currentPage}/${page.pageSize}`, query)
get<ResponsePage<ProblemItem>>(`${getPrefix(knowledgeId)}/${page.currentPage}/${page.pageSize}`, query)

/** 编辑单个知识库问题的内容。 */
const putProblem = (knowledgeId: string, problemId: string, content: string) =>
Expand Down
6 changes: 3 additions & 3 deletions ui/src/api/admin/system/shared-resources/knowledge/problem.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import { del, get, post, put } from '@/api/admin/core/request'
import type { ParamsPage, ResponsePage } from '@/api/admin/core/types'
import type { ParagraphProblem, ProblemBatchAssociationPayload, ProblemParagraph } from '@/api/types'
import type { ProblemItem, ProblemBatchAssociationPayload, ProblemParagraph } from '@/api/types'

const getPrefix = (knowledgeId: string) => `/system/shared/knowledge/${knowledgeId}/problem`

/** 批量创建知识库问题。 */
const postProblems = (knowledgeId: string, contents: string[]) => post<string[], void>(getPrefix(knowledgeId), contents)

/** 分页搜索知识库问题,供分段选择已有问题进行关联。 */
/** 分页搜索知识库问题,供问题列表及分段关联候选使用。 */
const getProblemsPage = (knowledgeId: string, page: ParamsPage, query?: { content?: string }) =>
get<ResponsePage<ParagraphProblem>>(`${getPrefix(knowledgeId)}/${page.currentPage}/${page.pageSize}`, query)
get<ResponsePage<ProblemItem>>(`${getPrefix(knowledgeId)}/${page.currentPage}/${page.pageSize}`, query)

/** 编辑单个知识库问题的内容。 */
const putProblem = (knowledgeId: string, problemId: string, content: string) =>
Expand Down
6 changes: 3 additions & 3 deletions ui/src/api/admin/workspace/knowledge/problem.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import { del, get, post, put } from '../../core/request'
import type { ParamsPage, ResponsePage } from '../../core/types'
import type { ParagraphProblem, ProblemBatchAssociationPayload, ProblemParagraph } from '@/api/types'
import type { ProblemItem, ProblemBatchAssociationPayload, ProblemParagraph } from '@/api/types'
import { getWorkspaceId } from '@/utils/resource-context'

const getPrefix = (knowledgeId: string) => `/workspace/${getWorkspaceId()}/knowledge/${knowledgeId}/problem`

/** 批量创建知识库问题。 */
const postProblems = (knowledgeId: string, contents: string[]) => post<string[], void>(getPrefix(knowledgeId), contents)

/** 分页搜索知识库问题,供分段选择已有问题进行关联。 */
/** 分页搜索知识库问题,供问题列表及分段关联候选使用。 */
const getProblemsPage = (knowledgeId: string, page: ParamsPage, query?: { content?: string }) =>
get<ResponsePage<ParagraphProblem>>(`${getPrefix(knowledgeId)}/${page.currentPage}/${page.pageSize}`, query)
get<ResponsePage<ProblemItem>>(`${getPrefix(knowledgeId)}/${page.currentPage}/${page.pageSize}`, query)

/** 编辑单个知识库问题的内容。 */
const putProblem = (knowledgeId: string, problemId: string, content: string) =>
Expand Down
4 changes: 2 additions & 2 deletions ui/src/api/admin/workspace/shared/knowledge/problem.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { get } from '../../../core/request'
import type { ParamsPage, ResponsePage } from '../../../core/types'
import type { ParagraphProblem } from '@/api/types'
import type { ProblemItem } from '@/api/types'
import { getWorkspaceId } from '@/utils/resource-context'

const getPrefix = (knowledgeId: string) => `/system/shared/workspace/${getWorkspaceId()}/knowledge/${knowledgeId}/problem`

/** 分页查询工作空间共享知识库的问题,只提供只读访问。 */
const getProblemsPage = (knowledgeId: string, page: ParamsPage, query?: { content?: string }) =>
get<ResponsePage<ParagraphProblem>>(`${getPrefix(knowledgeId)}/${page.currentPage}/${page.pageSize}`, query)
get<ResponsePage<ProblemItem>>(`${getPrefix(knowledgeId)}/${page.currentPage}/${page.pageSize}`, query)

export default { getProblemsPage }
9 changes: 9 additions & 0 deletions ui/src/api/types/paragraph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,15 @@ export interface ParagraphProblem {
content: string
}

/** 知识库问题列表,包含关联数量和召回统计。 */
export interface ProblemItem extends ParagraphProblem {
paragraph_count: number
hit_num: number
last_hit_time: string | null
create_time: string
update_time: string
}

/** 从问题详情查询到的关联分段,额外携带所属知识库与文档名称。 */
export interface ProblemParagraph extends ParagraphItem {
knowledge_id: string
Expand Down
16 changes: 14 additions & 2 deletions ui/src/views/VIEW_README.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@

`knowledge-detail/context.ts` 仅通过 `provideKnowledgeDetailContext` 和
`useKnowledgeDetailContext` 公开详情、更新能力、ID、来源导航及完整 API。
`resourceApi.knowledge / scheduledSync / document / paragraph / image / tags / termbase / chatUser / externalRetrieval` 按当前范围响应式读取,无需 `.value`,
`resourceApi.knowledge / scheduledSync / document / paragraph / image / tags / termbase / problem / chatUser / externalRetrieval` 按当前范围响应式读取,无需 `.value`,
不要在 setup 时解构具体 API。`workflow` 当前仅普通 Workspace 提供,其他范围不回退。
共有查询方法必填,部分范围缺失的方法可选调用;未发请求时不提示成功、不关闭、不刷新或修改快照,loading 正常清理。
不增加查询/维护双入口、接口别名或专属 Context hook,也不以方法存在性新增操作显隐规则。
Expand Down Expand Up @@ -207,9 +207,21 @@ System 资源管理或 System 共享 Knowledge API 的 `postKnowledgeHitTest`;
文档 `DocumentTags`、标签 Drawer 和批量 Action 各自维护添加弹窗;添加候选仅取前 100 组并本地搜索,
不禁选已关联值,后台去重。创建候选成功重载但不自动选择。删除仅解除文档关联,不删除知识库标签。

### 问题管理

`question/` 使用 `resourceApi.problem`,默认每页 20 条,不提供表头排序,搜索将 content 传给后端。
`ButtonCreateProblem` 内聚创建按钮和多行输入弹窗;快捷创建使用 `MkQuickCreate`,每个问题最多 256 字符。
单项与批量删除直接提交,删除后末页为空时回退到最后有效页;名称编辑复用 `MkEditName`。
点击数据行打开 `ProblemDetailDrawer`,勾选、名称编辑和操作按钮不触发行详情。
详情支持问题名称快捷编辑、关联分段卡片和跨页上一个/下一个;卡片复用 `.mk-paragraph-card` 样式,
点击打开已有 `ParagraphDialog`,悬浮取消关联按钮仅解除当前关联,成功刷新卡片与列表计数。
分段详情关闭后重查关联,保持编辑与解除关联后的数据一致。
单项、批量、详情的“关联分段”入口暂时禁用,待交互确认后接入,不创建关联弹窗或发送关联请求。
Workspace 共享只展示和搜索问题列表,不显示维护入口,也不打开缺少查询接口的问题详情。

## 文档分段

`document-detail/context.ts` 装配四范围完整 Document/Paragraph API 和返回导航,
`document-detail/context.ts` 装配四范围完整 Document/Paragraph/Problem API 和返回导航,
无注入时按当前路由创建上下文,供导入预览复用。页面提供上下文;Action 接收选定 Paragraph API,
`paragraph-dialog/` 直接读取上下文,不透传 API。共有查询必填,维护方法按真实差异可选。
Workspace 共享只读,隐藏创建、编辑、排序、关联修改及批量操作;缺少详情接口保留默认标题,不回退。
Expand Down
17 changes: 12 additions & 5 deletions ui/src/views/document-detail/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,18 @@ import ParagraphApi from '@/api/admin/workspace/knowledge/paragraph'
import SharedParagraphApi from '@/api/admin/workspace/shared/knowledge/paragraph'
import SystemResourceParagraphApi from '@/api/admin/system/resource-management/knowledge/paragraph'
import SystemSharedParagraphApi from '@/api/admin/system/shared-resources/knowledge/paragraph'
import ProblemApi from '@/api/admin/workspace/knowledge/problem'
import SharedProblemApi from '@/api/admin/workspace/shared/knowledge/problem'
import SystemResourceProblemApi from '@/api/admin/system/resource-management/knowledge/problem'
import SystemSharedProblemApi from '@/api/admin/system/shared-resources/knowledge/problem'
import type { ResourceScope } from '@/router/admin/types'

const resourceConfigs = {
workspace: { document: DocumentApi, paragraph: ParagraphApi },
'workspace-shared': { document: SharedDocumentApi, paragraph: SharedParagraphApi },
'system-resource': { document: SystemResourceDocumentApi, paragraph: SystemResourceParagraphApi },
'system-shared': { document: SystemSharedDocumentApi, paragraph: SystemSharedParagraphApi },
} satisfies Record<ResourceScope, { document: typeof SharedDocumentApi; paragraph: typeof SharedParagraphApi }>
workspace: { document: DocumentApi, paragraph: ParagraphApi, problem: ProblemApi },
'workspace-shared': { document: SharedDocumentApi, paragraph: SharedParagraphApi, problem: SharedProblemApi },
'system-resource': { document: SystemResourceDocumentApi, paragraph: SystemResourceParagraphApi, problem: SystemResourceProblemApi },
'system-shared': { document: SystemSharedDocumentApi, paragraph: SystemSharedParagraphApi, problem: SystemSharedProblemApi },
} satisfies Record<ResourceScope, { document: typeof SharedDocumentApi; paragraph: typeof SharedParagraphApi; problem: typeof SharedProblemApi }>

/** 共有查询方法保持必填,仅部分范围提供的方法标记为可选。 */
type ApiKeys<Api> = Api extends unknown ? keyof Api : never
Expand All @@ -41,6 +45,9 @@ function createDocumentDetailContext() {
get paragraph(): DocumentParagraphApi {
return resourceConfig.value.paragraph
},
get problem(): OptionalApiMethods<(typeof resourceConfigs)[ResourceScope]['problem']> {
return resourceConfig.value.problem
},
})

function handleBack() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
import { nextTick, onMounted, ref } from 'vue'
import type { SelectInstance } from 'element-plus'
import { useDocumentDetailContext } from '@/views/document-detail/context'
import ProblemApi from '@/api/admin/workspace/knowledge/problem'
import type { ParagraphProblem } from '@/api/types'
import type { ParagraphQuestionDraft } from '../types'
import { isWorkspaceSharedResource } from '@/utils/resource-context'
Expand All @@ -25,7 +24,8 @@ const optionLoading = ref(false)
function loadProblemOptions(query = '') {
optionLoading.value = true
return ProblemApi.getProblemsPage(props.knowledgeId, { currentPage: 1, pageSize: 100 }, query.trim() ? { content: query.trim() } : undefined)
return resourceApi.problem
.getProblemsPage(props.knowledgeId, { currentPage: 1, pageSize: 100 }, query.trim() ? { content: query.trim() } : undefined)
.then(({ records }) => {
problemOptions.value = records
})
Expand Down
18 changes: 17 additions & 1 deletion ui/src/views/knowledge-detail/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ import TermbaseApi from '@/api/admin/workspace/knowledge/termbase'
import SharedTermbaseApi from '@/api/admin/workspace/shared/knowledge/termbase'
import SystemResourceTermbaseApi from '@/api/admin/system/resource-management/knowledge/termbase'
import SystemSharedTermbaseApi from '@/api/admin/system/shared-resources/knowledge/termbase'
import ProblemApi from '@/api/admin/workspace/knowledge/problem'
import SharedProblemApi from '@/api/admin/workspace/shared/knowledge/problem'
import SystemResourceProblemApi from '@/api/admin/system/resource-management/knowledge/problem'
import SystemSharedProblemApi from '@/api/admin/system/shared-resources/knowledge/problem'
import WorkflowApi from '@/api/admin/workspace/knowledge/workflow'
import ScheduledSyncApi from '@/api/admin/workspace/knowledge/scheduled-sync'
import SharedScheduledSyncApi from '@/api/admin/workspace/shared/knowledge/scheduled-sync'
Expand All @@ -51,6 +55,7 @@ interface KnowledgeResourceApi {
readonly paragraph: OptionalApiMethods<
typeof ParagraphApi | typeof SharedParagraphApi | typeof SystemResourceParagraphApi | typeof SystemSharedParagraphApi
>
readonly problem: OptionalApiMethods<typeof ProblemApi | typeof SharedProblemApi | typeof SystemResourceProblemApi | typeof SystemSharedProblemApi>
readonly chatUser: OptionalApiMethods<
typeof ChatUserApi | typeof SharedChatUserApi | typeof SystemResourceChatUserApi | typeof SystemSharedChatUserApi
>
Expand Down Expand Up @@ -93,6 +98,7 @@ const resourceConfigs = {
image: ImageApi,
tags: TagsApi,
termbase: TermbaseApi,
problem: ProblemApi,
workflow: WorkflowApi,
},
listRouteName: 'workspace-knowledge-list',
Expand All @@ -107,6 +113,7 @@ const resourceConfigs = {
paragraph: SharedParagraphApi,
tags: SharedTagsApi,
termbase: SharedTermbaseApi,
problem: SharedProblemApi,
},
listRouteName: 'workspace-knowledge-list',
},
Expand All @@ -121,6 +128,7 @@ const resourceConfigs = {
image: SystemResourceImageApi,
tags: SystemResourceTagsApi,
termbase: SystemResourceTermbaseApi,
problem: SystemResourceProblemApi,
},
listRouteName: 'system-resource-knowledge',
},
Expand All @@ -134,6 +142,7 @@ const resourceConfigs = {
paragraph: SystemSharedParagraphApi,
tags: SystemSharedTagsApi,
termbase: SystemSharedTermbaseApi,
problem: SystemSharedProblemApi,
},
listRouteName: 'system-shared-knowledge',
},
Expand Down Expand Up @@ -183,6 +192,9 @@ function createKnowledgeDetailContext(state: KnowledgeDetailState): KnowledgeDet
get termbase() {
return resourceConfig.value.resourceApi.termbase
},
get problem() {
return resourceConfig.value.resourceApi.problem
},
get knowledge() {
return resourceConfig.value.resourceApi.knowledge
},
Expand Down Expand Up @@ -237,7 +249,11 @@ type ScopedResourceApi<Scope extends ResourceScope> = {
}

type ScopedKnowledgeDetailContext<Scope extends ResourceScope> = Omit<KnowledgeDetailContext, 'resourceApi'> & {
resourceApi: Omit<ScopedResourceApi<Scope>, 'scheduledSync' | 'tags' | 'termbase' | 'document' | 'chatUser' | 'externalRetrieval' | 'image'> & {
resourceApi: Omit<
ScopedResourceApi<Scope>,
'scheduledSync' | 'tags' | 'termbase' | 'problem' | 'document' | 'chatUser' | 'externalRetrieval' | 'image'
> & {
readonly problem: OptionalApiMethods<(typeof resourceConfigs)[Scope]['resourceApi']['problem']>
readonly image: KnowledgeResourceApi['image']
readonly chatUser: OptionalApiMethods<(typeof resourceConfigs)[Scope]['resourceApi']['chatUser']>
readonly scheduledSync: OptionalApiMethods<(typeof resourceConfigs)[Scope]['resourceApi']['scheduledSync']>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<script setup lang="ts">
import { computed, ref } from 'vue'
import { MsgSuccess, MsgWarning } from '@/utils/message'
import { useKnowledgeDetailContext } from '@/views/knowledge-detail/context'
defineOptions({ name: 'ButtonCreateProblem' })
const emit = defineEmits<{ refresh: [] }>()
const { knowledgeId, resourceApi } = useKnowledgeDetailContext()
const visible = ref(false)
const loading = ref(false)
const content = ref('')
const problems = computed(() =>
content.value
.split('\n')
.map((line) => line.trim())
.filter(Boolean),
)
function handleOpenCreateProblem() {
visible.value = true
}
function handleCreateProblems() {
if (loading.value || !problems.value.length) return
if (problems.value.some((problem) => problem.length > 256)) {
MsgWarning('每个问题最多输入 256 个字符')
return
}
loading.value = true
return (
resourceApi.problem.postProblems?.(knowledgeId.value, problems.value).then(() => {
MsgSuccess('创建成功')
visible.value = false
emit('refresh')
}) ?? Promise.resolve()
)
.catch(() => {
// 请求层提示错误,保留输入以便重试。
})
.finally(() => {
loading.value = false
})
}
function handleBeforeClose(done: () => void) {
if (!loading.value) done()
}
</script>

<template>
<!-- 创建问题 -->
<el-button type="primary" @click="handleOpenCreateProblem">
<MkIcon name="icon_add_outlined" />
<span>创建问题</span>
</el-button>
<MkDialog v-model="visible" title="创建问题" :before-close="handleBeforeClose" @closed="content = ''">
<el-input v-model="content" type="textarea" :rows="7" :disabled="loading" placeholder="请输入问题,一行一个,支持输入多个" />
<template #footer>
<!-- 取消创建 -->
<el-button plain :disabled="loading" @click="visible = false">取消</el-button>
<!-- 提交问题 -->
<el-button type="primary" :loading="loading" :disabled="!problems.length" @click="handleCreateProblems">创建</el-button>
</template>
</MkDialog>
</template>
Loading
Loading