feat: improve agent collaboration observability
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
"""
|
||||
import logging
|
||||
import time
|
||||
from datetime import datetime
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
from sqlalchemy import select
|
||||
@@ -45,7 +46,93 @@ class AgentService:
|
||||
for key, value in self._agent_collaboration_metadata(agent).items():
|
||||
collaboration.setdefault(key, value)
|
||||
enriched["collaboration"] = collaboration
|
||||
communication = dict(enriched.get("communication") or {})
|
||||
actor = dict(communication.get("actor") or {})
|
||||
for key in (
|
||||
"agent_id",
|
||||
"agent_code",
|
||||
"agent_name",
|
||||
"collaboration_role",
|
||||
"collaboration_mode",
|
||||
"model_id",
|
||||
):
|
||||
if collaboration.get(key):
|
||||
actor.setdefault(key, collaboration[key])
|
||||
communication.setdefault("event", enriched.get("type") or "")
|
||||
communication.setdefault("status", "failed" if enriched.get("type") == "error" else "completed")
|
||||
communication.setdefault("channel", "agent_chat")
|
||||
communication.setdefault("actor", actor)
|
||||
communication.setdefault(
|
||||
"message",
|
||||
enriched.get("content")
|
||||
or enriched.get("message")
|
||||
or enriched.get("accumulated_content")
|
||||
or "",
|
||||
)
|
||||
enriched["communication"] = communication
|
||||
return enriched
|
||||
|
||||
@staticmethod
|
||||
def _event_to_reasoning_step(event: Dict[str, Any]) -> Optional[Dict[str, Any]]:
|
||||
event_type = event.get('type')
|
||||
if event_type not in {
|
||||
'action',
|
||||
'annotation_reply',
|
||||
'error',
|
||||
'knowledge_retrieval',
|
||||
'loop_complete',
|
||||
'loop_iteration_complete',
|
||||
'loop_iteration_error',
|
||||
'loop_iteration_start',
|
||||
'node_complete',
|
||||
'node_start',
|
||||
'observation',
|
||||
'parallel_complete',
|
||||
'parallel_start',
|
||||
'thought',
|
||||
'tool_call',
|
||||
'tool_result',
|
||||
'waiting_input',
|
||||
}:
|
||||
return None
|
||||
|
||||
collaboration = event.get('collaboration') or {}
|
||||
communication = event.get('communication') or {}
|
||||
config = event.get('waiting_config') or event.get('config') or {}
|
||||
|
||||
content = (
|
||||
event.get('node_label')
|
||||
or event.get('content')
|
||||
or communication.get('message')
|
||||
or config.get('title')
|
||||
or config.get('question')
|
||||
or event.get('node_type')
|
||||
or event_type
|
||||
)
|
||||
|
||||
step = {
|
||||
'type': event_type,
|
||||
'content': content,
|
||||
'tool': event.get('tool') or '',
|
||||
'params': event.get('params') or config,
|
||||
'node_id': event.get('node_id') or '',
|
||||
'node_type': event.get('node_type') or '',
|
||||
'branch_id': event.get('branch_id') or collaboration.get('branch_id') or '',
|
||||
'branch_label': event.get('branch_label') or collaboration.get('branch_label') or '',
|
||||
'agent_code': collaboration.get('agent_code') or event.get('agent_code') or '',
|
||||
'agent_name': collaboration.get('agent_name') or event.get('agent_name') or '',
|
||||
'model': collaboration.get('model') or event.get('model') or '',
|
||||
'model_id': collaboration.get('model_id') or event.get('model_id') or '',
|
||||
'subflow_name': collaboration.get('subflow_name') or event.get('subflow_name') or '',
|
||||
'from_subflow': bool(collaboration.get('from_subflow') or event.get('from_subflow')),
|
||||
'collaboration_role': collaboration.get('collaboration_role') or '',
|
||||
'collaboration_mode': collaboration.get('collaboration_mode') or event.get('collaboration_mode') or '',
|
||||
'communication': communication,
|
||||
'output': event.get('outputs') or event.get('output'),
|
||||
'status': 'running' if event_type in {'node_start', 'parallel_start', 'waiting_input', 'loop_iteration_start'} else 'completed',
|
||||
'timestamp': datetime.now().isoformat(),
|
||||
}
|
||||
return {key: value for key, value in step.items() if value not in (None, '', {}, [])}
|
||||
|
||||
async def chat(
|
||||
self,
|
||||
@@ -209,16 +296,31 @@ class AgentService:
|
||||
)
|
||||
self._db.add(assistant_msg)
|
||||
await self._db.flush()
|
||||
reasoning_steps: List[Dict[str, Any]] = []
|
||||
|
||||
yield {
|
||||
yield self._attach_agent_collaboration({
|
||||
'type': 'start',
|
||||
'conversation_id': str(conversation.id),
|
||||
'message_id': str(assistant_msg.id),
|
||||
}
|
||||
}, agent)
|
||||
|
||||
try:
|
||||
# 构建系统提示词
|
||||
system_prompt = self._build_system_prompt_simple(agent)
|
||||
model_id = await self._resolve_agent_model_id(agent)
|
||||
if not model_id:
|
||||
raise ValueError('未找到可用的 chat 模型,请先在模型配置中启用一个模型')
|
||||
|
||||
event = self._attach_agent_collaboration({
|
||||
'type': 'thought',
|
||||
'content': '智能体开始分析用户需求并调用默认 chat 模型',
|
||||
'model_id': model_id,
|
||||
}, agent)
|
||||
step = self._event_to_reasoning_step(event)
|
||||
if step:
|
||||
reasoning_steps.append(step)
|
||||
assistant_msg.reasoning_steps = reasoning_steps.copy()
|
||||
yield event
|
||||
|
||||
# 标注直接回复检查(高相似度时跳过 LLM,直接返回标注答案)
|
||||
annotation_reply = await self._check_annotation_direct_reply(agent, user_message)
|
||||
@@ -257,11 +359,16 @@ class AgentService:
|
||||
knowledge_context = await self._retrieve_knowledge_context(agent, user_message)
|
||||
if knowledge_context:
|
||||
system_prompt = self._inject_knowledge_context(system_prompt, knowledge_context)
|
||||
yield {
|
||||
event = {
|
||||
'type': 'knowledge_retrieval',
|
||||
'content': knowledge_context['summary'],
|
||||
'result_count': knowledge_context['result_count'],
|
||||
}
|
||||
step = self._event_to_reasoning_step(event)
|
||||
if step:
|
||||
reasoning_steps.append(step)
|
||||
assistant_msg.reasoning_steps = reasoning_steps.copy()
|
||||
yield event
|
||||
|
||||
# 获取对话历史
|
||||
history = await self._get_conversation_history(conversation, limit=10)
|
||||
@@ -297,6 +404,7 @@ class AgentService:
|
||||
'type': 'llm_chunk',
|
||||
'content': chunk.content,
|
||||
'accumulated_content': accumulated_content,
|
||||
'model_id': model_id,
|
||||
}
|
||||
|
||||
if chunk.is_finished:
|
||||
@@ -317,6 +425,7 @@ class AgentService:
|
||||
yield {
|
||||
'type': 'answer',
|
||||
'content': final_answer,
|
||||
'model_id': model_id,
|
||||
}
|
||||
|
||||
# 更新助手消息
|
||||
@@ -325,6 +434,7 @@ class AgentService:
|
||||
assistant_msg.status = 'completed'
|
||||
assistant_msg.total_tokens = total_tokens
|
||||
assistant_msg.elapsed_time = elapsed_time
|
||||
assistant_msg.reasoning_steps = reasoning_steps.copy()
|
||||
|
||||
# 更新对话统计
|
||||
conversation.message_count = (conversation.message_count or 0) + 2
|
||||
@@ -352,6 +462,10 @@ class AgentService:
|
||||
logger.exception(f'Agent chat error: {e}')
|
||||
assistant_msg.status = 'failed'
|
||||
assistant_msg.error_message = str(e)
|
||||
error_step = self._event_to_reasoning_step({'type': 'error', 'content': str(e)})
|
||||
if error_step:
|
||||
reasoning_steps.append(error_step)
|
||||
assistant_msg.reasoning_steps = reasoning_steps.copy()
|
||||
await self._db.commit()
|
||||
yield {
|
||||
'type': 'error',
|
||||
@@ -438,6 +552,7 @@ class AgentService:
|
||||
)
|
||||
self._db.add(assistant_msg)
|
||||
await self._db.flush()
|
||||
reasoning_steps: List[Dict[str, Any]] = []
|
||||
|
||||
yield {
|
||||
'type': 'start',
|
||||
@@ -507,6 +622,25 @@ class AgentService:
|
||||
output = (event.get('outputs') or {}).get('output', '')
|
||||
if output:
|
||||
final_content = output
|
||||
|
||||
step = self._event_to_reasoning_step(event)
|
||||
if step:
|
||||
existing_index = next(
|
||||
(
|
||||
index
|
||||
for index, item in enumerate(reasoning_steps)
|
||||
if item.get('node_id')
|
||||
and item.get('node_id') == step.get('node_id')
|
||||
and item.get('type') == 'node_start'
|
||||
and step.get('type') == 'node_complete'
|
||||
),
|
||||
-1,
|
||||
)
|
||||
if existing_index >= 0:
|
||||
reasoning_steps[existing_index] = step
|
||||
else:
|
||||
reasoning_steps.append(step)
|
||||
assistant_msg.reasoning_steps = reasoning_steps.copy()
|
||||
|
||||
yield event
|
||||
else:
|
||||
@@ -573,6 +707,25 @@ class AgentService:
|
||||
output = (event.get('outputs') or {}).get('output', '')
|
||||
if output:
|
||||
final_content = output
|
||||
|
||||
step = self._event_to_reasoning_step(event)
|
||||
if step:
|
||||
existing_index = next(
|
||||
(
|
||||
index
|
||||
for index, item in enumerate(reasoning_steps)
|
||||
if item.get('node_id')
|
||||
and item.get('node_id') == step.get('node_id')
|
||||
and item.get('type') == 'node_start'
|
||||
and step.get('type') == 'node_complete'
|
||||
),
|
||||
-1,
|
||||
)
|
||||
if existing_index >= 0:
|
||||
reasoning_steps[existing_index] = step
|
||||
else:
|
||||
reasoning_steps.append(step)
|
||||
assistant_msg.reasoning_steps = reasoning_steps.copy()
|
||||
|
||||
yield event
|
||||
|
||||
@@ -585,6 +738,7 @@ class AgentService:
|
||||
assistant_msg.status = 'completed'
|
||||
assistant_msg.elapsed_time = elapsed_time
|
||||
assistant_msg.total_tokens = total_tokens
|
||||
assistant_msg.reasoning_steps = reasoning_steps.copy()
|
||||
|
||||
# 更新对话统计
|
||||
conversation.message_count = (conversation.message_count or 0) + 2
|
||||
@@ -614,6 +768,10 @@ class AgentService:
|
||||
logger.exception(f'Dialog flow error: {e}')
|
||||
assistant_msg.status = 'failed'
|
||||
assistant_msg.error_message = str(e)
|
||||
error_step = self._event_to_reasoning_step({'type': 'error', 'content': str(e)})
|
||||
if error_step:
|
||||
reasoning_steps.append(error_step)
|
||||
assistant_msg.reasoning_steps = reasoning_steps.copy()
|
||||
await self._db.commit()
|
||||
yield {
|
||||
'type': 'error',
|
||||
|
||||
Reference in New Issue
Block a user