feat: improve agent collaboration observability
This commit is contained in:
@@ -281,6 +281,9 @@ export interface ExecutionLogEntry {
|
||||
error?: string;
|
||||
elapsed_time?: number;
|
||||
tokens_used?: number;
|
||||
branch?: string;
|
||||
branch_label?: string;
|
||||
metadata?: Record<string, any>;
|
||||
}
|
||||
|
||||
export interface WorkflowRunListItem {
|
||||
|
||||
@@ -194,6 +194,21 @@ const getStepColor = (step: ReasoningStep) => {
|
||||
|
||||
const getStepMetaItems = (step: ReasoningStep) => {
|
||||
const items: string[] = [];
|
||||
const communication = step.communication;
|
||||
const actor = communication?.actor || {};
|
||||
const target = communication?.target || {};
|
||||
const actorName =
|
||||
actor.agent_name ||
|
||||
actor.agent_code ||
|
||||
actor.node_label ||
|
||||
step.agent_name ||
|
||||
step.agent_code;
|
||||
const targetName = target.agent_name || target.agent_code || target.node_label;
|
||||
if (actorName && targetName) {
|
||||
items.push(`交接: ${actorName} -> ${targetName}`);
|
||||
} else if (communication?.channel) {
|
||||
items.push(`通道: ${communication.channel}`);
|
||||
}
|
||||
if (step.agent_name || step.agent_code) {
|
||||
items.push(`智能体: ${step.agent_name || step.agent_code}`);
|
||||
}
|
||||
@@ -206,6 +221,9 @@ const getStepMetaItems = (step: ReasoningStep) => {
|
||||
if (step.model || step.model_id) {
|
||||
items.push(`模型: ${step.model || step.model_id}`);
|
||||
}
|
||||
if (communication?.message && communication.message !== step.content) {
|
||||
items.push(`消息: ${communication.message}`);
|
||||
}
|
||||
return items;
|
||||
};
|
||||
|
||||
|
||||
@@ -72,6 +72,16 @@ export interface ReasoningStep {
|
||||
from_subflow?: boolean;
|
||||
collaboration_role?: string;
|
||||
collaboration_mode?: string;
|
||||
communication?: {
|
||||
actor?: Record<string, any>;
|
||||
branch_id?: string;
|
||||
branch_label?: string;
|
||||
channel?: string;
|
||||
event?: string;
|
||||
message?: string;
|
||||
status?: string;
|
||||
target?: Record<string, any>;
|
||||
};
|
||||
output?: any;
|
||||
/** 步骤状态:running 执行中,completed 已完成 */
|
||||
status?: 'completed' | 'running';
|
||||
|
||||
@@ -750,6 +750,7 @@ onUnmounted(() => {
|
||||
:enable-image="enableImage"
|
||||
:enable-voice="enableVoice"
|
||||
:enable-feedback="enableFeedback"
|
||||
:show-reasoning-steps="true"
|
||||
:show-clear-button="showClearButton"
|
||||
:empty-text="emptyText"
|
||||
:welcome-config="welcomeConfig"
|
||||
|
||||
@@ -231,6 +231,17 @@ export function useChatApi(props: AiChatPanelProps): {
|
||||
params: step.params,
|
||||
node_id: step.node_id,
|
||||
node_type: step.node_type,
|
||||
branch_id: step.branch_id,
|
||||
branch_label: step.branch_label,
|
||||
agent_code: step.agent_code,
|
||||
agent_name: step.agent_name,
|
||||
model: step.model,
|
||||
model_id: step.model_id,
|
||||
subflow_name: step.subflow_name,
|
||||
from_subflow: step.from_subflow,
|
||||
collaboration_role: step.collaboration_role,
|
||||
collaboration_mode: step.collaboration_mode,
|
||||
communication: step.communication,
|
||||
output: step.output,
|
||||
status: step.status,
|
||||
timestamp: step.timestamp,
|
||||
|
||||
@@ -172,6 +172,7 @@ export function useEventHandler(config: EventHandlerConfig) {
|
||||
|
||||
const buildReasoningMeta = (event: StreamEvent): Partial<ReasoningStep> => {
|
||||
const collaboration = event.collaboration || {};
|
||||
const communication = event.communication || {};
|
||||
return {
|
||||
branch_id: event.branch_id,
|
||||
branch_label: event.branch_label,
|
||||
@@ -187,6 +188,7 @@ export function useEventHandler(config: EventHandlerConfig) {
|
||||
collaboration_role:
|
||||
collaboration.collaboration_role || collaboration.role || event.collaboration_role,
|
||||
collaboration_mode: event.collaboration_mode || collaboration.collaboration_mode,
|
||||
communication,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -230,6 +232,35 @@ export function useEventHandler(config: EventHandlerConfig) {
|
||||
break;
|
||||
}
|
||||
|
||||
case 'annotation_reply':
|
||||
case 'knowledge_retrieval':
|
||||
case 'observation':
|
||||
case 'thought':
|
||||
case 'tool_call':
|
||||
case 'tool_result': {
|
||||
currentSteps.value.push({
|
||||
type: event.type as ReasoningStep['type'],
|
||||
content:
|
||||
event.content ||
|
||||
event.message ||
|
||||
event.tool ||
|
||||
event.type,
|
||||
tool: event.tool,
|
||||
params: event.params,
|
||||
result: event.result,
|
||||
node_id: event.node_id,
|
||||
node_type: event.node_type,
|
||||
...buildReasoningMeta(event),
|
||||
status: 'completed',
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
updateAssistantMessage(msgId, {
|
||||
reasoning_steps: [...currentSteps.value],
|
||||
...buildMessageMeta(event),
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
case 'answer': {
|
||||
const initialMsg = messages.value.find((m) => m.id === msgId);
|
||||
if (initialMsg && !initialMsg.content?.trim()) {
|
||||
|
||||
@@ -118,11 +118,25 @@ const collaborationTimeline = computed(() => {
|
||||
.map((log, index) => {
|
||||
const meta = getLogMeta(log);
|
||||
const collaboration = meta.collaboration || {};
|
||||
const agentCode = meta.agent_code || collaboration.agent_code;
|
||||
const agentName = meta.agent_name || collaboration.agent_name || agentCode;
|
||||
const branch = log.branch_label || log.branch || collaboration.branch_label;
|
||||
const communication = meta.communication || {};
|
||||
const actor = communication.actor || {};
|
||||
const target = communication.target || {};
|
||||
const agentCode = actor.agent_code || meta.agent_code || collaboration.agent_code;
|
||||
const agentName =
|
||||
actor.agent_name ||
|
||||
actor.agent_code ||
|
||||
meta.agent_name ||
|
||||
collaboration.agent_name ||
|
||||
agentCode;
|
||||
const branch =
|
||||
communication.branch_label ||
|
||||
log.branch_label ||
|
||||
log.branch ||
|
||||
collaboration.branch_label;
|
||||
const subflowName = collaboration.subflow_name || meta.subflow_name;
|
||||
const model = meta.model || collaboration.model || meta.model_id || collaboration.model_id;
|
||||
const targetName =
|
||||
target.agent_name || target.agent_code || target.node_label || '';
|
||||
const title =
|
||||
agentName ||
|
||||
subflowName ||
|
||||
@@ -131,8 +145,10 @@ const collaborationTimeline = computed(() => {
|
||||
log.node_id ||
|
||||
`#${index + 1}`;
|
||||
const summaryParts = [
|
||||
targetName ? `交接给:${targetName}` : '',
|
||||
branch ? `分支:${branch}` : '',
|
||||
subflowName ? `子流程:${subflowName}` : '',
|
||||
communication.channel ? `通道:${communication.channel}` : '',
|
||||
model ? `模型:${model}` : '',
|
||||
].filter(Boolean);
|
||||
|
||||
@@ -143,9 +159,10 @@ const collaborationTimeline = computed(() => {
|
||||
subtitle: log.node_label || log.node_id,
|
||||
status: log.status,
|
||||
summary: summaryParts.join(' · '),
|
||||
message: communication.message || previewValue(log.error || log.output),
|
||||
elapsed: log.elapsed_time,
|
||||
tokens: log.tokens_used || 0,
|
||||
hasSignal: Boolean(agentName || branch || subflowName || model),
|
||||
hasSignal: Boolean(agentName || branch || subflowName || model || communication.channel),
|
||||
};
|
||||
})
|
||||
.filter((item) => item.hasSignal);
|
||||
@@ -248,6 +265,35 @@ function getLogMeta(log: any) {
|
||||
return log?.metadata || {};
|
||||
}
|
||||
|
||||
function getCommunication(log: any) {
|
||||
return getLogMeta(log).communication || {};
|
||||
}
|
||||
|
||||
function getCollaboration(log: any) {
|
||||
return getLogMeta(log).collaboration || {};
|
||||
}
|
||||
|
||||
function getActorName(log: any) {
|
||||
const communication = getCommunication(log);
|
||||
const actor = communication.actor || {};
|
||||
const collaboration = getCollaboration(log);
|
||||
return (
|
||||
actor.agent_name ||
|
||||
actor.agent_code ||
|
||||
collaboration.agent_name ||
|
||||
collaboration.agent_code ||
|
||||
actor.node_label ||
|
||||
log.node_label ||
|
||||
log.node_id ||
|
||||
'-'
|
||||
);
|
||||
}
|
||||
|
||||
function getTargetName(log: any) {
|
||||
const target = getCommunication(log).target || {};
|
||||
return target.agent_name || target.agent_code || target.node_label || '';
|
||||
}
|
||||
|
||||
function selectLog(nodeId?: string) {
|
||||
if (!nodeId) return;
|
||||
activeNodeId.value = nodeId;
|
||||
@@ -468,29 +514,56 @@ defineExpose({ open });
|
||||
{{ log.error }}
|
||||
</div>
|
||||
<div
|
||||
v-if="getLogMeta(log).agent_code"
|
||||
v-if="getCollaboration(log).agent_code || getCommunication(log).channel"
|
||||
class="border-border bg-muted/40 grid grid-cols-2 gap-2 rounded-md border p-2"
|
||||
>
|
||||
<div>
|
||||
<div class="text-muted-foreground">
|
||||
{{ $t('ai-platform.workflowRuns.detail.agent') }}
|
||||
执行者
|
||||
</div>
|
||||
<div class="font-medium">
|
||||
{{ getLogMeta(log).agent_name || getLogMeta(log).agent_code }}
|
||||
{{ getActorName(log) }}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="text-muted-foreground">
|
||||
{{ $t('ai-platform.workflowRuns.detail.agentCode') }}
|
||||
交接目标
|
||||
</div>
|
||||
<div class="font-medium">
|
||||
{{ getTargetName(log) || '-' }}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="text-muted-foreground">
|
||||
协作通道
|
||||
</div>
|
||||
<div class="break-words">
|
||||
{{ getCommunication(log).channel || getCollaboration(log).collaboration_mode || '-' }}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="text-muted-foreground">分支/子流程</div>
|
||||
<div class="break-words">
|
||||
{{
|
||||
getCommunication(log).branch_label ||
|
||||
getCollaboration(log).branch_label ||
|
||||
getCollaboration(log).subflow_name ||
|
||||
'-'
|
||||
}}
|
||||
</div>
|
||||
<div class="font-mono">{{ getLogMeta(log).agent_code }}</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="text-muted-foreground">
|
||||
{{ $t('ai-platform.workflowRuns.detail.model') }}
|
||||
</div>
|
||||
<div class="break-words">
|
||||
{{ getLogMeta(log).model || getLogMeta(log).model_id || '-' }}
|
||||
{{
|
||||
getLogMeta(log).model ||
|
||||
getCollaboration(log).model ||
|
||||
getLogMeta(log).model_id ||
|
||||
getCollaboration(log).model_id ||
|
||||
'-'
|
||||
}}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
@@ -502,6 +575,15 @@ defineExpose({ open });
|
||||
{{ getLogMeta(log).completion_tokens || 0 }}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="getCommunication(log).message"
|
||||
class="col-span-2"
|
||||
>
|
||||
<div class="text-muted-foreground">通信摘要</div>
|
||||
<div class="break-words">
|
||||
{{ getCommunication(log).message }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="text-muted-foreground mb-1 font-medium">
|
||||
|
||||
Reference in New Issue
Block a user