feat: Enhance Kol Generation Service with web search and knowledge group support

- Added `EnableWebSearch` and `KnowledgeGroupIDs` fields to `KolGenerationSubmitRequest`.
- Updated `Submit` method in `KolGenerationService` to handle new request fields.
- Integrated web search and knowledge group validation based on card configuration.
- Introduced caching mechanisms in `KolPackageService`, `KolPromptService`, and `KolSubscriptionAdminService` to improve performance.
- Implemented knowledge context resolution in `KolGenerationWorker` to enrich prompts with relevant knowledge snippets.
- Added utility functions for handling card configuration in both backend and frontend.
- Created tests for knowledge extraction and rendering to ensure accuracy and reliability.
This commit is contained in:
2026-04-18 13:47:32 +08:00
parent 3ef0807456
commit b2605abd6a
27 changed files with 2051 additions and 171 deletions
@@ -10,9 +10,10 @@ import KolVariableConfig from "./KolVariableConfig.vue";
import KolPromptEditArea from "./KolPromptEditArea.vue";
import KolDynamicForm from "./KolDynamicForm.vue";
import { kolManageApi } from "@/lib/api";
import type { KolVariableDefinition } from "@geo/shared-types";
import type { KolCardConfig, KolVariableDefinition } from "@geo/shared-types";
import { formatError } from "@/lib/errors";
import { buildKolPlatformOptions } from "@/lib/kol-platform-options";
import { mergeKolCardConfig, parseKolCardConfig } from "@/lib/kol-card-config";
import { syncKolVariablesWithContent } from "@/lib/kol-placeholders";
const props = defineProps<{
@@ -31,10 +32,15 @@ const variables = ref<KolVariableDefinition[]>([]);
const previewOpen = ref(false);
const previewValues = ref<Record<string, any>>({});
const aiTaskId = ref<string | null>(null);
const variableConfigRef = ref<{ focusVariable: (key: string) => void } | null>(null);
const cardConfig = ref<KolCardConfig>({});
const variableConfigRef = ref<{
focusVariable: (key: string, options?: { focusInput?: boolean }) => void;
} | null>(null);
const metadataForm = reactive({
name: "",
platform_hint: "通用",
allow_web_search: false,
allow_user_knowledge: true,
});
const { data: promptDetail } = useQuery({
@@ -55,10 +61,16 @@ watch(
() => promptDetail.value,
(detail) => {
if (detail) {
const nextCardConfig = detail.latest_card_config_json ?? {};
const parsedCardConfig = parseKolCardConfig(nextCardConfig);
metadataForm.name = detail.name;
metadataForm.platform_hint = detail.platform_hint || "通用";
metadataForm.allow_web_search = parsedCardConfig.allow_web_search;
metadataForm.allow_user_knowledge = parsedCardConfig.allow_user_knowledge;
content.value = detail.latest_prompt_content ?? "";
variables.value = detail.latest_schema_json?.variables ?? [];
cardConfig.value = { ...nextCardConfig };
}
},
{ immediate: true }
@@ -148,7 +160,10 @@ function buildPromptPayload(nextVariables: KolVariableDefinition[] = variables.v
sort_order: promptDetail.value?.sort_order,
content: content.value,
schema: { variables: nextVariables },
card_config: {},
card_config: mergeKolCardConfig(cardConfig.value, {
allow_web_search: metadataForm.allow_web_search,
allow_user_knowledge: metadataForm.allow_user_knowledge,
}),
};
}
@@ -214,7 +229,7 @@ function handlePreview() {
async function handleFocusVariable(key: string) {
syncVariablesFromContent();
await nextTick();
variableConfigRef.value?.focusVariable(key);
variableConfigRef.value?.focusVariable(key, { focusInput: false });
}
function getSelectPopupContainer(triggerNode: HTMLElement) {
@@ -288,6 +303,20 @@ function statusLabel(status?: string): string {
:get-popup-container="getSelectPopupContainer"
/>
</a-form-item>
<a-form-item label="生成文章时允许联网搜索">
<div class="editor-switch-field">
<a-switch v-model:checked="metadataForm.allow_web_search" />
<span class="editor-switch-hint">默认关闭关闭后用户在生成页无法启用联网搜索</span>
</div>
</a-form-item>
<a-form-item label="生成文章时允许用户使用自己的知识库">
<div class="editor-switch-field">
<a-switch v-model:checked="metadataForm.allow_user_knowledge" />
<span class="editor-switch-hint">默认开启关闭后用户在生成页无法选择自己的知识库</span>
</div>
</a-form-item>
</a-form>
</div>
@@ -432,6 +461,19 @@ function statusLabel(status?: string): string {
background: #f0f0f0;
}
.editor-switch-field {
display: flex;
align-items: center;
gap: 12px;
min-height: 32px;
}
.editor-switch-hint {
color: #667085;
font-size: 13px;
line-height: 1.5;
}
@media (max-width: 1200px) {
.editor-meta-form {
grid-template-columns: 1fr 1fr;