fix: execute multica template agents
This commit is contained in:
@@ -2,8 +2,11 @@
|
||||
模板渲染节点
|
||||
"""
|
||||
import logging
|
||||
import time
|
||||
from typing import Any, Dict
|
||||
|
||||
from sqlalchemy import select
|
||||
|
||||
from ..base import BaseNode, NodeContext, NodeResult
|
||||
from ..registry import NodeRegistry
|
||||
|
||||
@@ -61,6 +64,144 @@ class TemplateNode(BaseNode):
|
||||
success=False,
|
||||
error=str(e),
|
||||
)
|
||||
|
||||
async def execute_async(self, context: NodeContext) -> NodeResult:
|
||||
"""配置 agent_code 时,以智能体身份执行当前模板任务。"""
|
||||
agent_code = (self.config.get('agent_code') or '').strip()
|
||||
if not agent_code:
|
||||
return self.execute(context)
|
||||
|
||||
start_time = time.time()
|
||||
try:
|
||||
if not context.db_session:
|
||||
return NodeResult(success=False, error='智能体模板节点需要数据库会话')
|
||||
|
||||
from ai_platform.models import Agent
|
||||
from ai_platform.services.llm_service import LLMService
|
||||
|
||||
result = await context.db_session.execute(
|
||||
select(Agent).where(
|
||||
Agent.code == agent_code,
|
||||
Agent.is_deleted == False,
|
||||
Agent.status == 'published',
|
||||
)
|
||||
)
|
||||
agent = result.scalar_one_or_none()
|
||||
if not agent:
|
||||
return NodeResult(success=False, error=f'智能体不存在或未发布: {agent_code}')
|
||||
|
||||
model_id = self.config.get('model_id') or agent.model_id
|
||||
if not model_id:
|
||||
return NodeResult(success=False, error=f'智能体未配置模型: {agent.name}({agent.code})')
|
||||
|
||||
template = self.config.get('template', '')
|
||||
rendered_prompt = context.resolve_template(template)
|
||||
if not rendered_prompt.strip():
|
||||
return NodeResult(success=False, error='智能体模板节点缺少任务内容')
|
||||
|
||||
messages = [
|
||||
{'role': 'system', 'content': self._build_agent_system_prompt(agent)},
|
||||
{'role': 'user', 'content': self._build_agent_user_prompt(context, rendered_prompt)},
|
||||
]
|
||||
response = await LLMService(context.db_session).chat_async(
|
||||
model_id=str(model_id),
|
||||
messages=messages,
|
||||
temperature=self.config.get(
|
||||
'temperature',
|
||||
agent.temperature if agent.temperature is not None else 0.7,
|
||||
),
|
||||
max_tokens=self.config.get('max_tokens', agent.max_tokens or 2048),
|
||||
)
|
||||
|
||||
elapsed_time = int((time.time() - start_time) * 1000)
|
||||
output_variable = self.config.get('output_variable') or f'{agent.code}_result'
|
||||
output_variables = {
|
||||
output_variable: response.content,
|
||||
f'{output_variable}_agent_code': agent.code,
|
||||
f'{output_variable}_agent_name': agent.name,
|
||||
f'{output_variable}_tokens': response.total_tokens,
|
||||
}
|
||||
|
||||
return NodeResult(
|
||||
success=True,
|
||||
output=response.content,
|
||||
output_variables=output_variables,
|
||||
tokens_used=response.total_tokens,
|
||||
elapsed_time=elapsed_time,
|
||||
metadata={
|
||||
'agent_code': agent.code,
|
||||
'agent_name': agent.name,
|
||||
'model_id': str(model_id),
|
||||
'model': response.model,
|
||||
'prompt_tokens': response.prompt_tokens,
|
||||
'completion_tokens': response.completion_tokens,
|
||||
'output_variable': output_variable,
|
||||
'frontend_output_variables': output_variables,
|
||||
},
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
logger.exception(f'智能体模板节点执行失败: {e}')
|
||||
return NodeResult(
|
||||
success=False,
|
||||
error=str(e),
|
||||
elapsed_time=int((time.time() - start_time) * 1000),
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _build_agent_system_prompt(agent: Any) -> str:
|
||||
base_prompt = agent.system_prompt or ''
|
||||
persona = agent.persona or {}
|
||||
|
||||
if not base_prompt and isinstance(persona, dict):
|
||||
parts = []
|
||||
if persona.get('role'):
|
||||
parts.append(persona['role'])
|
||||
if persona.get('skills'):
|
||||
parts.append('你擅长:' + '、'.join(str(item) for item in persona['skills']) + '。')
|
||||
if persona.get('constraints'):
|
||||
parts.append('注意事项:\n' + '\n'.join(f'- {item}' for item in persona['constraints']))
|
||||
if persona.get('background'):
|
||||
parts.append(str(persona['background']))
|
||||
base_prompt = '\n\n'.join(parts)
|
||||
|
||||
if not base_prompt:
|
||||
base_prompt = '你是一个智能体,请用中文完成当前任务。'
|
||||
|
||||
return (
|
||||
f'{base_prompt}\n\n'
|
||||
'你正在作为流程编排中的协作智能体执行当前步骤。'
|
||||
'请只输出本步骤的结论、交付证据、风险和下一步,避免空泛说明。'
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _format_context_value(value: Any, max_length: int = 1200) -> str:
|
||||
if isinstance(value, str):
|
||||
text = value
|
||||
else:
|
||||
import json
|
||||
try:
|
||||
text = json.dumps(value, ensure_ascii=False, indent=2)
|
||||
except TypeError:
|
||||
text = str(value)
|
||||
return text if len(text) <= max_length else f'{text[:max_length]}...'
|
||||
|
||||
def _build_agent_user_prompt(self, context: NodeContext, rendered_prompt: str) -> str:
|
||||
context_lines = []
|
||||
for key, value in context.variables.items():
|
||||
if key.startswith('_') or key.endswith('_tokens') or key.endswith('_agent_code') or key.endswith('_agent_name'):
|
||||
continue
|
||||
context_lines.append(f'- {key}: {self._format_context_value(value)}')
|
||||
if len('\n'.join(context_lines)) > 6000:
|
||||
context_lines.append('- 其余上下文因长度限制已省略')
|
||||
break
|
||||
|
||||
sections = [f'当前步骤任务:\n{rendered_prompt}']
|
||||
if context.previous_output:
|
||||
sections.append(f'上一节点输出:\n{self._format_context_value(context.previous_output, 2000)}')
|
||||
if context_lines:
|
||||
sections.append('可用工作流上下文:\n' + '\n'.join(context_lines))
|
||||
return '\n\n'.join(sections)
|
||||
|
||||
@classmethod
|
||||
def get_config_schema(cls) -> Dict[str, Any]:
|
||||
@@ -79,6 +220,16 @@ class TemplateNode(BaseNode):
|
||||
'title': '输出变量名',
|
||||
'default': 'template_result',
|
||||
},
|
||||
'agent_code': {
|
||||
'type': 'string',
|
||||
'title': '智能体编码',
|
||||
'description': '配置后会以该智能体身份调用模型执行模板任务',
|
||||
},
|
||||
'model_id': {
|
||||
'type': 'string',
|
||||
'title': '覆盖模型 ID',
|
||||
'description': '为空时使用智能体默认模型',
|
||||
},
|
||||
},
|
||||
'required': ['template'],
|
||||
}
|
||||
|
||||
@@ -64,6 +64,10 @@ def _make_execution_log_entry(
|
||||
}
|
||||
|
||||
|
||||
def _node_result_metadata(result: NodeResult) -> dict:
|
||||
return dict(result.metadata or {})
|
||||
|
||||
|
||||
def _snapshot_node_inputs(context: NodeContext) -> dict:
|
||||
return {
|
||||
'variables': dict(context.variables),
|
||||
@@ -535,6 +539,7 @@ class AIWorkflowService:
|
||||
node_type,
|
||||
status='completed',
|
||||
output=result.output,
|
||||
metadata=_node_result_metadata(result),
|
||||
inputs=copy.deepcopy(node_inputs),
|
||||
))
|
||||
break
|
||||
@@ -563,6 +568,7 @@ class AIWorkflowService:
|
||||
error=result.error,
|
||||
elapsed_time=elapsed,
|
||||
tokens_used=result.tokens_used,
|
||||
metadata=_node_result_metadata(result),
|
||||
inputs=copy.deepcopy(node_inputs),
|
||||
))
|
||||
|
||||
@@ -725,6 +731,7 @@ class AIWorkflowService:
|
||||
'error': result.error,
|
||||
'elapsed_time': elapsed,
|
||||
'tokens_used': result.tokens_used,
|
||||
'metadata': _node_result_metadata(result),
|
||||
'branch': branch_start_id,
|
||||
})
|
||||
|
||||
@@ -940,6 +947,7 @@ class AIWorkflowService:
|
||||
'error': result.error,
|
||||
'elapsed_time': elapsed,
|
||||
'tokens_used': result.tokens_used,
|
||||
'metadata': _node_result_metadata(result),
|
||||
'branch': branch_id,
|
||||
})
|
||||
|
||||
@@ -1228,6 +1236,7 @@ class AIWorkflowService:
|
||||
'error': result.error,
|
||||
'elapsed_time': elapsed,
|
||||
'tokens_used': result.tokens_used,
|
||||
'metadata': _node_result_metadata(result),
|
||||
'loop_iteration': iteration,
|
||||
})
|
||||
|
||||
@@ -1733,6 +1742,7 @@ class AIWorkflowService:
|
||||
error=result.error,
|
||||
elapsed_time=elapsed,
|
||||
tokens_used=result.tokens_used,
|
||||
metadata=_node_result_metadata(result),
|
||||
inputs=copy.deepcopy(node_inputs),
|
||||
)
|
||||
logs.append(log_entry)
|
||||
@@ -2201,6 +2211,7 @@ class AIWorkflowService:
|
||||
'status': 'waiting',
|
||||
'output': result.output,
|
||||
'elapsed_time': elapsed,
|
||||
'metadata': _node_result_metadata(result),
|
||||
}
|
||||
logs.append(log_entry)
|
||||
|
||||
@@ -2235,6 +2246,7 @@ class AIWorkflowService:
|
||||
'error': result.error,
|
||||
'elapsed_time': elapsed,
|
||||
'tokens_used': result.tokens_used,
|
||||
'metadata': _node_result_metadata(result),
|
||||
}
|
||||
logs.append(log_entry)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user