feat: 工作流记忆与内置工具、知你客服脚本、Agent管理技能展示与能力配置、文档与Windows启动脚本;忽略 redis_temp 二进制目录

Made-with: Cursor
This commit is contained in:
renjianbo
2026-04-08 11:44:24 +08:00
parent 599b8f2851
commit bd3f8be781
66 changed files with 10104 additions and 469 deletions

View File

@@ -0,0 +1,76 @@
/**
* 从 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 日志' }
]
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
}