AI写作:新增素材来源选项(知识库/技能检索)
写作表单新增「素材来源」下拉:知识库(默认 web/intranet 检索)/ 技能检索(用已启用技能编排检索)。用户选择后素材收集专家首次检索 据此决定检索方式。 - state 新增 material_source 通道(缺省 knowledge_base,兼容老 session) - researcher 首次检索(无干预)时按来源决定 is_skill_search; PAUSE-1「启用技能检索」按钮仍独立有效 - 前端 AIWritingRequest.materialSource + 表单下拉 + hook 提交 material_source Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
169346cdbd
commit
5d0bafd651
5 files changed
+49
-2
No files matched your search
@@ -29,8 +29,15 @@ const INPUT_CLASS =
|
|||||||
|
|
||||||
const DEFAULT_KEYWORD_COUNT = 1;
|
const DEFAULT_KEYWORD_COUNT = 1;
|
||||||
const DEFAULT_AUDIENCE = '普通读者';
|
const DEFAULT_AUDIENCE = '普通读者';
|
||||||
|
const DEFAULT_MATERIAL_SOURCE: 'knowledge_base' | 'skill' = 'knowledge_base';
|
||||||
const MAX_INTENT_LENGTH = 500;
|
const MAX_INTENT_LENGTH = 500;
|
||||||
|
|
||||||
|
// 素材来源选项:知识库=素材专家默认 web/intranet 检索;技能检索=用技能编排去检索
|
||||||
|
const MATERIAL_SOURCE_OPTIONS: Array<{ value: 'knowledge_base' | 'skill'; label: string }> = [
|
||||||
|
{ value: 'knowledge_base', label: '知识库' },
|
||||||
|
{ value: 'skill', label: '技能检索' },
|
||||||
|
];
|
||||||
|
|
||||||
const FIELD_LABEL_CLASS = 'text-[16px] font-semibold leading-none text-[#222]';
|
const FIELD_LABEL_CLASS = 'text-[16px] font-semibold leading-none text-[#222]';
|
||||||
const FIELD_TRIGGER_CLASS =
|
const FIELD_TRIGGER_CLASS =
|
||||||
'h-12 w-full rounded-xl border-[#e8dedd] bg-white text-sm shadow-sm';
|
'h-12 w-full rounded-xl border-[#e8dedd] bg-white text-sm shadow-sm';
|
||||||
@@ -45,6 +52,8 @@ export function AIWritingForm({
|
|||||||
const [audience, setAudience] = useState('普通读者');
|
const [audience, setAudience] = useState('普通读者');
|
||||||
const [wordCountInput, setWordCountInput] = useState('800');
|
const [wordCountInput, setWordCountInput] = useState('800');
|
||||||
const [strictMode, setStrictMode] = useState(false);
|
const [strictMode, setStrictMode] = useState(false);
|
||||||
|
const [materialSource, setMaterialSource] =
|
||||||
|
useState<'knowledge_base' | 'skill'>(DEFAULT_MATERIAL_SOURCE);
|
||||||
const [modelName, setModelName] = useState('');
|
const [modelName, setModelName] = useState('');
|
||||||
const { models, isLoading: modelsLoading } = useModels();
|
const { models, isLoading: modelsLoading } = useModels();
|
||||||
|
|
||||||
@@ -111,6 +120,7 @@ export function AIWritingForm({
|
|||||||
wordCountTarget: wordCount,
|
wordCountTarget: wordCount,
|
||||||
keywordCount: DEFAULT_KEYWORD_COUNT,
|
keywordCount: DEFAULT_KEYWORD_COUNT,
|
||||||
writingMode: hideAdvancedFields ? 'loose' : strictMode ? 'strict' : 'loose',
|
writingMode: hideAdvancedFields ? 'loose' : strictMode ? 'strict' : 'loose',
|
||||||
|
materialSource,
|
||||||
modelName: selectedModel?.name || modelName || undefined,
|
modelName: selectedModel?.name || modelName || undefined,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -178,6 +188,29 @@ export function AIWritingForm({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className="mt-4 space-y-2">
|
||||||
|
<label className={FIELD_LABEL_CLASS}>素材来源</label>
|
||||||
|
<Select
|
||||||
|
value={materialSource}
|
||||||
|
onValueChange={(v) => setMaterialSource(v as 'knowledge_base' | 'skill')}
|
||||||
|
disabled={disabled}
|
||||||
|
>
|
||||||
|
<SelectTrigger className={FIELD_TRIGGER_CLASS}>
|
||||||
|
<SelectValue placeholder="选择素材来源" />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
{MATERIAL_SOURCE_OPTIONS.map((opt) => (
|
||||||
|
<SelectItem key={opt.value} value={opt.value} className="text-sm">
|
||||||
|
{opt.label}
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
<p className="text-xs leading-relaxed text-muted-foreground/80">
|
||||||
|
知识库:素材专家默认检索方式;技能检索:用已启用的技能去编排检索。
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="mt-4 space-y-2">
|
<div className="mt-4 space-y-2">
|
||||||
<label className={FIELD_LABEL_CLASS}>写作模型</label>
|
<label className={FIELD_LABEL_CLASS}>写作模型</label>
|
||||||
<Select
|
<Select
|
||||||
|
|||||||
@@ -747,6 +747,7 @@ export function useAIWriting() {
|
|||||||
word_count_target: request.wordCountTarget,
|
word_count_target: request.wordCountTarget,
|
||||||
keyword_count: Math.max(1, Math.min(10, request.keywordCount)),
|
keyword_count: Math.max(1, Math.min(10, request.keywordCount)),
|
||||||
writing_mode: request.writingMode === 'strict' ? 'strict' : 'loose',
|
writing_mode: request.writingMode === 'strict' ? 'strict' : 'loose',
|
||||||
|
material_source: request.materialSource === 'skill' ? 'skill' : 'knowledge_base',
|
||||||
revision_count: 0,
|
revision_count: 0,
|
||||||
max_revisions: 3,
|
max_revisions: 3,
|
||||||
status: 'researching',
|
status: 'researching',
|
||||||
|
|||||||
@@ -228,6 +228,8 @@ export interface AIWritingRequest {
|
|||||||
keywordCount: number;
|
keywordCount: number;
|
||||||
/** 写作模式:strict=严格依据素材 / loose=可结合通用知识(默认 loose) */
|
/** 写作模式:strict=严格依据素材 / loose=可结合通用知识(默认 loose) */
|
||||||
writingMode: 'strict' | 'loose';
|
writingMode: 'strict' | 'loose';
|
||||||
|
/** 素材来源:knowledge_base=知识库(默认检索)/ skill=技能检索(默认 knowledge_base) */
|
||||||
|
materialSource: 'knowledge_base' | 'skill';
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── 流式审核条目 ──────────────────────────────────────────────────────────────
|
// ── 流式审核条目 ──────────────────────────────────────────────────────────────
|
||||||
|
|||||||
+10
-2
@@ -116,11 +116,19 @@ async def researcher_node(state: AIWritingState, config: RunnableConfig) -> dict
|
|||||||
is_user_query_search = action == "re_search" and bool(user_query)
|
is_user_query_search = action == "re_search" and bool(user_query)
|
||||||
# 兼容旧版"追加关键词"调用方(如果还有 client 传 extra_keywords 数组而非 user_query)
|
# 兼容旧版"追加关键词"调用方(如果还有 client 传 extra_keywords 数组而非 user_query)
|
||||||
is_append_only = action == "re_search" and not user_query and bool(extra_keywords)
|
is_append_only = action == "re_search" and not user_query and bool(extra_keywords)
|
||||||
# PAUSE-1 素材确认卡用户点「启用技能检索」:
|
# 用户在写作表单选择的素材来源:knowledge_base(默认 web/intranet 检索)/ skill(技能检索)。
|
||||||
|
# 首次检索(没有用户干预)时按它决定检索方式;缺省 knowledge_base。
|
||||||
|
material_source = (state.get("material_source") or "knowledge_base").strip()
|
||||||
|
is_first_run = not intervention
|
||||||
|
is_initial_skill_source = is_first_run and material_source == "skill"
|
||||||
|
# 技能检索触发条件(任一即可):
|
||||||
|
# - 首次检索且表单选了「技能检索」来源
|
||||||
|
# - PAUSE-1 素材确认卡用户点「启用技能检索」按钮
|
||||||
|
# 技能检索流程:
|
||||||
# - 不再走 web/intranet 检索,改用 SkillSearchProvider
|
# - 不再走 web/intranet 检索,改用 SkillSearchProvider
|
||||||
# - 顺序调用候选技能 + 早停(第一个技能够就停,不够再加下一个)
|
# - 顺序调用候选技能 + 早停(第一个技能够就停,不够再加下一个)
|
||||||
# - 旧 material_package 完全丢弃(覆盖式替换,让用户对比 web vs skill 结果)
|
# - 旧 material_package 完全丢弃(覆盖式替换,让用户对比 web vs skill 结果)
|
||||||
is_skill_search = action == "enable_skill_search"
|
is_skill_search = action == "enable_skill_search" or is_initial_skill_source
|
||||||
|
|
||||||
# 用户指定的检索关键词数量(1~10),决定生成多少个检索词、检索多少个方向
|
# 用户指定的检索关键词数量(1~10),决定生成多少个检索词、检索多少个方向
|
||||||
keyword_count = _resolve_keyword_count(state.get("keyword_count"))
|
keyword_count = _resolve_keyword_count(state.get("keyword_count"))
|
||||||
|
|||||||
@@ -132,6 +132,9 @@ class AIWritingState(TypedDict):
|
|||||||
keyword_count: int
|
keyword_count: int
|
||||||
# 写作模式:strict=严格依据素材 / loose=可结合通用知识(默认 loose)
|
# 写作模式:strict=严格依据素材 / loose=可结合通用知识(默认 loose)
|
||||||
writing_mode: str
|
writing_mode: str
|
||||||
|
# 素材来源:knowledge_base=知识库(默认 web/intranet 检索)/ skill=技能检索。
|
||||||
|
# 用户在写作表单选择,首次检索时由 researcher 节点据此决定检索方式(缺省 knowledge_base)。
|
||||||
|
material_source: str
|
||||||
# 用户在写作表单选定的主模型。大纲/写作/审核严格使用它(不回退默认)。
|
# 用户在写作表单选定的主模型。大纲/写作/审核严格使用它(不回退默认)。
|
||||||
# 由 intent_parser 在首次 invocation 从 configurable.model_name 捕获进 state,
|
# 由 intent_parser 在首次 invocation 从 configurable.model_name 捕获进 state,
|
||||||
# 经 checkpoint 持久化 —— interrupt/resume 是新 invocation、其 config 不带原
|
# 经 checkpoint 持久化 —— interrupt/resume 是新 invocation、其 config 不带原
|
||||||
|
|||||||
Reference in new issue
Block a user