feat: 全新智能体功能
- 基于先进智能体框架,增加智能体编排功能 - 增加智能体聊天,并对接持久化
This commit is contained in:
@@ -0,0 +1,371 @@
|
||||
import type {
|
||||
AgentCapabilityKind,
|
||||
AgentDraftState,
|
||||
AgentInfo,
|
||||
AgentKnowledgeBinding,
|
||||
AgentToolBinding,
|
||||
AgentValidationIssue,
|
||||
} from '../types';
|
||||
|
||||
import {computed, reactive} from 'vue';
|
||||
|
||||
const BASE_NODE_ID = 'agent-base';
|
||||
const SAFE_TOOL_NAME_PATTERN = /^[a-zA-Z0-9_-]+$/;
|
||||
|
||||
function createLocalId(prefix: string) {
|
||||
return `${prefix}-${Date.now()}-${Math.random().toString(16).slice(2, 8)}`;
|
||||
}
|
||||
|
||||
function isSafeToolName(name?: string) {
|
||||
return SAFE_TOOL_NAME_PATTERN.test(String(name || ''));
|
||||
}
|
||||
|
||||
function buildFallbackToolName(prefix: string, resource?: Record<string, any>) {
|
||||
const id = resource?.id ? String(resource.id) : createLocalId(prefix);
|
||||
return `${prefix}_${id}`;
|
||||
}
|
||||
|
||||
function resolveToolName(
|
||||
kind: Exclude<AgentCapabilityKind, 'knowledge'>,
|
||||
resource?: Record<string, any>,
|
||||
) {
|
||||
if (isSafeToolName(resource?.englishName)) {
|
||||
return String(resource?.englishName);
|
||||
}
|
||||
if (isSafeToolName(resource?.name)) {
|
||||
return String(resource?.name);
|
||||
}
|
||||
return buildFallbackToolName(
|
||||
kind === 'workflow' ? 'workflow' : 'plugin',
|
||||
resource,
|
||||
);
|
||||
}
|
||||
|
||||
function normalizeBindingToolName(binding: AgentToolBinding) {
|
||||
if (isSafeToolName(binding.toolName)) {
|
||||
return String(binding.toolName);
|
||||
}
|
||||
const kind =
|
||||
String(binding.toolType || '').toUpperCase() === 'WORKFLOW'
|
||||
? 'workflow'
|
||||
: 'plugin';
|
||||
const resource = {
|
||||
...(binding.resourceSnapshot || {}),
|
||||
...(binding.resourceSummary || {}),
|
||||
id:
|
||||
binding.targetId ||
|
||||
binding.resourceSummary?.id ||
|
||||
binding.resourceSnapshot?.id,
|
||||
};
|
||||
return resolveToolName(kind, resource);
|
||||
}
|
||||
|
||||
export function createEmptyAgent(): AgentInfo {
|
||||
return {
|
||||
name: '未命名智能体',
|
||||
description: '',
|
||||
avatar: '',
|
||||
categoryId: '',
|
||||
modelId: '',
|
||||
promptConfigJson: { systemPrompt: '' },
|
||||
memoryConfigJson: {
|
||||
compressionParameter: {
|
||||
enabled: true,
|
||||
msgThreshold: 12,
|
||||
lastKeep: 8,
|
||||
minCompressionTokenThreshold: 6000,
|
||||
},
|
||||
},
|
||||
status: 1,
|
||||
};
|
||||
}
|
||||
|
||||
function normalizeAgent(agent?: AgentInfo): AgentInfo {
|
||||
const source = agent || {};
|
||||
const memoryConfig = source.memoryConfigJson || {};
|
||||
const compressionParameter = memoryConfig.compressionParameter || {};
|
||||
const defaultCompressionParameter =
|
||||
createEmptyAgent().memoryConfigJson?.compressionParameter || {};
|
||||
const legacyMsgThreshold = memoryConfig.maxAttachedMessageCount;
|
||||
return {
|
||||
...createEmptyAgent(),
|
||||
...source,
|
||||
promptConfigJson: {
|
||||
systemPrompt: '',
|
||||
...source.promptConfigJson,
|
||||
},
|
||||
memoryConfigJson: {
|
||||
...memoryConfig,
|
||||
compressionParameter: {
|
||||
enabled: true,
|
||||
...compressionParameter,
|
||||
msgThreshold:
|
||||
compressionParameter.msgThreshold ??
|
||||
legacyMsgThreshold ??
|
||||
defaultCompressionParameter.msgThreshold,
|
||||
lastKeep:
|
||||
compressionParameter.lastKeep ?? defaultCompressionParameter.lastKeep,
|
||||
minCompressionTokenThreshold:
|
||||
compressionParameter.minCompressionTokenThreshold ??
|
||||
defaultCompressionParameter.minCompressionTokenThreshold,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function normalizeKnowledgeBinding(
|
||||
binding: AgentKnowledgeBinding,
|
||||
index: number,
|
||||
): AgentKnowledgeBinding {
|
||||
return {
|
||||
...binding,
|
||||
enabled: binding.enabled !== false,
|
||||
localId:
|
||||
binding.localId || String(binding.id || createLocalId('knowledge')),
|
||||
optionsJson: {
|
||||
limit: 5,
|
||||
scoreThreshold: 0.5,
|
||||
...binding.optionsJson,
|
||||
},
|
||||
retrievalMode: binding.retrievalMode || 'HYBRID',
|
||||
sortNo: binding.sortNo ?? index + 1,
|
||||
};
|
||||
}
|
||||
|
||||
function normalizeToolBinding(
|
||||
binding: AgentToolBinding,
|
||||
index: number,
|
||||
): AgentToolBinding {
|
||||
return {
|
||||
...binding,
|
||||
enabled: binding.enabled !== false,
|
||||
hitlEnabled: Boolean(binding.hitlEnabled),
|
||||
localId:
|
||||
binding.localId ||
|
||||
String(
|
||||
binding.id ||
|
||||
createLocalId(String(binding.toolType || 'tool').toLowerCase()),
|
||||
),
|
||||
toolName: normalizeBindingToolName(binding),
|
||||
optionsJson: binding.optionsJson || {},
|
||||
sortNo: binding.sortNo ?? index + 1,
|
||||
};
|
||||
}
|
||||
|
||||
export function useAgentDesignerState() {
|
||||
const state = reactive<AgentDraftState>({
|
||||
agent: createEmptyAgent(),
|
||||
knowledgeBindings: [],
|
||||
toolBindings: [],
|
||||
selectedNodeId: BASE_NODE_ID,
|
||||
panelMode: 'base',
|
||||
dirty: false,
|
||||
});
|
||||
|
||||
const selectedCapability = computed(() => {
|
||||
if (state.selectedNodeId.startsWith('knowledge:')) {
|
||||
const localId = state.selectedNodeId.slice('knowledge:'.length);
|
||||
return {
|
||||
kind: 'knowledge' as AgentCapabilityKind,
|
||||
binding: state.knowledgeBindings.find(
|
||||
(item) => item.localId === localId,
|
||||
),
|
||||
};
|
||||
}
|
||||
if (state.selectedNodeId.startsWith('tool:')) {
|
||||
const localId = state.selectedNodeId.slice('tool:'.length);
|
||||
const binding = state.toolBindings.find(
|
||||
(item) => item.localId === localId,
|
||||
);
|
||||
return {
|
||||
kind:
|
||||
String(binding?.toolType || '').toUpperCase() === 'WORKFLOW'
|
||||
? ('workflow' as AgentCapabilityKind)
|
||||
: ('plugin' as AgentCapabilityKind),
|
||||
binding,
|
||||
};
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
|
||||
function markDirty() {
|
||||
state.dirty = true;
|
||||
}
|
||||
|
||||
function reset(agent?: AgentInfo) {
|
||||
state.agent = normalizeAgent(agent);
|
||||
state.knowledgeBindings = (agent?.knowledgeBindings || []).map(
|
||||
(binding, index) => normalizeKnowledgeBinding(binding, index),
|
||||
);
|
||||
state.toolBindings = (agent?.toolBindings || []).map((binding, index) =>
|
||||
normalizeToolBinding(binding, index),
|
||||
);
|
||||
state.selectedNodeId = BASE_NODE_ID;
|
||||
state.panelMode = 'base';
|
||||
state.dirty = false;
|
||||
}
|
||||
|
||||
function selectBase() {
|
||||
state.selectedNodeId = BASE_NODE_ID;
|
||||
state.panelMode = 'base';
|
||||
}
|
||||
|
||||
function selectNode(nodeId: string) {
|
||||
state.selectedNodeId = nodeId;
|
||||
state.panelMode = nodeId === BASE_NODE_ID ? 'base' : 'capability';
|
||||
}
|
||||
|
||||
function openTryout() {
|
||||
state.panelMode = 'tryout';
|
||||
}
|
||||
|
||||
function addKnowledgeNode(resource?: Record<string, any>) {
|
||||
const binding = normalizeKnowledgeBinding(
|
||||
{
|
||||
knowledgeId: resource?.id ? String(resource.id) : '',
|
||||
resourceSummary: resource || {},
|
||||
},
|
||||
state.knowledgeBindings.length,
|
||||
);
|
||||
state.knowledgeBindings.push(binding);
|
||||
state.selectedNodeId = `knowledge:${binding.localId}`;
|
||||
state.panelMode = 'capability';
|
||||
markDirty();
|
||||
}
|
||||
|
||||
function addToolNode(
|
||||
kind: Exclude<AgentCapabilityKind, 'knowledge'>,
|
||||
resource?: Record<string, any>,
|
||||
) {
|
||||
const toolType = kind === 'workflow' ? 'WORKFLOW' : 'PLUGIN';
|
||||
const binding = normalizeToolBinding(
|
||||
{
|
||||
toolType,
|
||||
targetId: resource?.id ? String(resource.id) : '',
|
||||
toolName: resolveToolName(kind, resource),
|
||||
resourceSummary: resource || {},
|
||||
},
|
||||
state.toolBindings.length,
|
||||
);
|
||||
state.toolBindings.push(binding);
|
||||
state.selectedNodeId = `tool:${binding.localId}`;
|
||||
state.panelMode = 'capability';
|
||||
markDirty();
|
||||
}
|
||||
|
||||
function removeSelectedCapability() {
|
||||
const selected = selectedCapability.value;
|
||||
if (!selected?.binding?.localId) return;
|
||||
if (selected.kind === 'knowledge') {
|
||||
state.knowledgeBindings = state.knowledgeBindings.filter(
|
||||
(item) => item.localId !== selected.binding?.localId,
|
||||
);
|
||||
} else {
|
||||
state.toolBindings = state.toolBindings.filter(
|
||||
(item) => item.localId !== selected.binding?.localId,
|
||||
);
|
||||
}
|
||||
selectBase();
|
||||
markDirty();
|
||||
}
|
||||
|
||||
function validate(): AgentValidationIssue[] {
|
||||
const issues: AgentValidationIssue[] = [];
|
||||
if (!String(state.agent.name || '').trim()) {
|
||||
issues.push({
|
||||
nodeId: BASE_NODE_ID,
|
||||
field: 'name',
|
||||
message: '请填写 Agent 名称',
|
||||
});
|
||||
}
|
||||
if (!state.agent.modelId) {
|
||||
issues.push({
|
||||
nodeId: BASE_NODE_ID,
|
||||
field: 'modelId',
|
||||
message: '请选择模型',
|
||||
});
|
||||
}
|
||||
state.knowledgeBindings.forEach((binding) => {
|
||||
if (!binding.knowledgeId) {
|
||||
issues.push({
|
||||
nodeId: `knowledge:${binding.localId}`,
|
||||
field: 'knowledgeId',
|
||||
message: '请选择知识库',
|
||||
});
|
||||
}
|
||||
});
|
||||
state.toolBindings.forEach((binding) => {
|
||||
const nodeId = `tool:${binding.localId}`;
|
||||
if (!binding.targetId) {
|
||||
issues.push({ nodeId, field: 'targetId', message: '请选择能力资源' });
|
||||
}
|
||||
if (!String(binding.toolName || '').trim()) {
|
||||
issues.push({ nodeId, field: 'toolName', message: '请填写工具名称' });
|
||||
} else if (!isSafeToolName(binding.toolName)) {
|
||||
issues.push({
|
||||
nodeId,
|
||||
field: 'toolName',
|
||||
message: '工具名称只能包含英文、数字、下划线或中划线',
|
||||
});
|
||||
}
|
||||
});
|
||||
return issues;
|
||||
}
|
||||
|
||||
function buildPayloadAgent(): AgentInfo {
|
||||
const memoryConfigJson = state.agent.memoryConfigJson || {};
|
||||
const compressionParameter = memoryConfigJson.compressionParameter || {};
|
||||
const { maxAttachedMessageCount, ...restMemoryConfigJson } =
|
||||
memoryConfigJson;
|
||||
return {
|
||||
...state.agent,
|
||||
memoryConfigJson: {
|
||||
...restMemoryConfigJson,
|
||||
compressionParameter: {
|
||||
...compressionParameter,
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
status: state.agent.status ?? 1,
|
||||
};
|
||||
}
|
||||
|
||||
function buildKnowledgePayload(agentId?: number | string) {
|
||||
return state.knowledgeBindings.map((binding, index) => ({
|
||||
...binding,
|
||||
agentId,
|
||||
enabled: binding.enabled !== false,
|
||||
sortNo: index + 1,
|
||||
}));
|
||||
}
|
||||
|
||||
function buildToolPayload(agentId?: number | string) {
|
||||
return state.toolBindings.map((binding, index) => ({
|
||||
...binding,
|
||||
agentId,
|
||||
enabled: binding.enabled !== false,
|
||||
hitlEnabled: Boolean(binding.hitlEnabled),
|
||||
sortNo: index + 1,
|
||||
}));
|
||||
}
|
||||
|
||||
reset();
|
||||
|
||||
return {
|
||||
BASE_NODE_ID,
|
||||
state,
|
||||
selectedCapability,
|
||||
addKnowledgeNode,
|
||||
addToolNode,
|
||||
buildKnowledgePayload,
|
||||
buildPayloadAgent,
|
||||
buildToolPayload,
|
||||
markDirty,
|
||||
openTryout,
|
||||
removeSelectedCapability,
|
||||
reset,
|
||||
selectBase,
|
||||
selectNode,
|
||||
validate,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user