- New PromptTemplatePicker component for browsing 13 preset prompt templates - AgentConfig.vue: "Load from library" button for system prompt - Agents.vue: "Create from Prompt template" entry with agent node + RAG memory - seed_prompt_templates.py: 13 preset templates (客服/研发/教育/内容/分析/创意/健康医疗) - agent_call tool: agents can delegate tasks to other agents (19th builtin tool) - Created 全能助手 (general orchestrator) and 家庭医生助手 agents - Switch template-created agents from type:llm to type:agent for full ReAct + RAG Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
78 lines
2.9 KiB
TypeScript
78 lines
2.9 KiB
TypeScript
/**
|
||
* 从 Agent 工作流中提取 / 写入 LLM 节点的工具(skills)配置。
|
||
*/
|
||
import type { WorkflowNode } from '@/types'
|
||
|
||
/** 与后端 tools_bootstrap 内置工具一致,用于列表展示与配置勾选 */
|
||
export const BUILTIN_SKILL_OPTIONS: { name: string; label: string }[] = [
|
||
{ name: 'http_request', label: 'HTTP 请求' },
|
||
{ name: 'file_read', label: '读文件' },
|
||
{ name: 'file_write', label: '写文件' },
|
||
{ name: 'text_analyze', label: '文本分析' },
|
||
{ name: 'datetime', label: '日期时间' },
|
||
{ name: 'math_calculate', label: '数学计算' },
|
||
{ name: 'system_info', label: '系统信息' },
|
||
{ name: 'json_process', label: 'JSON 处理' },
|
||
{ name: 'database_query', label: '数据库查询' },
|
||
{ name: 'adb_log', label: 'ADB 日志' },
|
||
{ name: 'agent_call', label: '调用 Agent' },
|
||
]
|
||
|
||
export const BUILTIN_SKILL_LABELS: Record<string, string> = Object.fromEntries(
|
||
BUILTIN_SKILL_OPTIONS.map((o) => [o.name, o.label])
|
||
)
|
||
|
||
function isLlmNode(n: WorkflowNode): boolean {
|
||
const t = (n.type || '').toLowerCase()
|
||
const dt = (n.data?.type || '').toLowerCase()
|
||
return t === 'llm' || t === 'template' || dt === 'llm'
|
||
}
|
||
|
||
/** 工作流中所有 LLM 节点上已选工具的并集(去重排序) */
|
||
export function extractSkillToolNames(workflow_config: { nodes?: WorkflowNode[] } | null | undefined): string[] {
|
||
const set = new Set<string>()
|
||
for (const n of workflow_config?.nodes || []) {
|
||
if (!isLlmNode(n)) continue
|
||
const raw = n.data?.tools ?? n.data?.selected_tools
|
||
if (Array.isArray(raw)) {
|
||
raw.forEach((x) => set.add(String(x)))
|
||
}
|
||
}
|
||
return Array.from(set).sort()
|
||
}
|
||
|
||
/**
|
||
* 作为「能力配置」写入时的目标节点:优先 llm-unified,否则第一个带工具的 LLM,否则第一个 LLM。
|
||
*/
|
||
export function findPrimaryLlmNodeForTools(nodes: WorkflowNode[] | undefined): WorkflowNode | null {
|
||
if (!nodes?.length) return null
|
||
const byId = nodes.find((n) => n.id === 'llm-unified' && isLlmNode(n))
|
||
if (byId) return byId
|
||
const withTools = nodes.find(
|
||
(n) =>
|
||
isLlmNode(n) &&
|
||
(n.data?.enable_tools === true ||
|
||
(Array.isArray(n.data?.tools) && n.data.tools.length > 0) ||
|
||
(Array.isArray(n.data?.selected_tools) && n.data.selected_tools.length > 0))
|
||
)
|
||
if (withTools) return withTools
|
||
return nodes.find((n) => isLlmNode(n)) || null
|
||
}
|
||
|
||
export function patchWorkflowSkillTools(
|
||
workflow_config: { nodes: WorkflowNode[]; edges: unknown[] },
|
||
toolNames: string[]
|
||
): { nodes: WorkflowNode[]; edges: unknown[] } {
|
||
const wf = JSON.parse(JSON.stringify(workflow_config)) as { nodes: WorkflowNode[]; edges: unknown[] }
|
||
const target = findPrimaryLlmNodeForTools(wf.nodes)
|
||
if (!target) return wf
|
||
|
||
const names = [...new Set(toolNames)].filter(Boolean).sort()
|
||
if (!target.data) target.data = {}
|
||
target.data.enable_tools = names.length > 0
|
||
target.data.tools = names
|
||
target.data.selected_tools = names
|
||
|
||
return wf
|
||
}
|