fix: 修复 5 项确定 bug + Provider UX 重做 + 文档统一
Bug fixes: - fix(dao): AsyncSession.delete 补齐漏掉的 await(provider/user/individual 共 4 处) - fix(worker): result.data.output → result.output.output(pydantic-ai 1.x API 适配) - fix(api): 删除 create_worker_from_template 死端点(ORM 字段不匹配必崩) - fix(api): /provider/test 按 provider_type 分支适配 Anthropic/Gemini/OpenAI 三种协议 - fix(chat): SSE 流式聊天在 distributed 模式 fallback 到非流式,避免 asyncio.Queue 序列化崩溃 Features (previously unstaged): - feat(provider): Provider 管理页重做(品牌图标、5 种类型、Test Connection、编辑模式) - feat(provider): 新增 Gemini provider_type 支持 - feat(workflow): Finalize 节点输出 blackboard 摘要 + 失败原因;步骤完成/失败实时推送 SSE - feat(i18n): regulatory_node 提示词从路由模式改为直接对话模式(中英双语) - feat(consciousness): dynamic_prompt 支持 locale 国际化 - feat(logs): SystemLogsView 自动刷新 + 暂停按钮 Docs: - docs: README/README-EN 统一为"开源通用多 Agent 协作平台"口径 - docs: ROADMAP 按 v0.1.x / v0.2.x / v0.3.x 重组 - docs: project.md 重写为结构化项目介绍 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
+17
-6
@@ -163,15 +163,15 @@ async def stream_chat_message(
|
||||
request: Request,
|
||||
token_data: TokenData = Depends(Accessor.get_current_user),
|
||||
):
|
||||
"""SSE 流式聊天端点:通过 regulatory_node agent 流式输出,支持工具调用。"""
|
||||
"""SSE 流式聊天端点:standalone 模式下逐 token 流式输出;distributed 模式 fallback 到整段回复。"""
|
||||
from kilostar.utils.standalone_proxy import _STANDALONE
|
||||
|
||||
postgres_database = ray_actor_hook("postgres_database").postgres_database
|
||||
|
||||
# 存用户消息
|
||||
await postgres_database.add_chat_message.remote(
|
||||
chat_id=chat_id, message=request_body.message, message_owner="user"
|
||||
)
|
||||
|
||||
# 构造 MessageRequest payload
|
||||
payload = MessageRequest(
|
||||
platform="client",
|
||||
user_name=token_data.user_id,
|
||||
@@ -180,9 +180,21 @@ async def stream_chat_message(
|
||||
)
|
||||
|
||||
regulatory_node = ray_actor_hook("regulatory_node").regulatory_node
|
||||
token_queue = asyncio.Queue()
|
||||
|
||||
# stream_working.remote() returns an asyncio.Task in standalone mode
|
||||
if not _STANDALONE:
|
||||
async def fallback_generator():
|
||||
resp = await regulatory_node.working.remote(payload)
|
||||
full_response = resp.reply_message if resp else ""
|
||||
if full_response:
|
||||
await postgres_database.add_chat_message.remote(
|
||||
chat_id=chat_id, message=full_response, message_owner="regulatory_node"
|
||||
)
|
||||
yield f"data: {json.dumps({'token': full_response})}\n\n"
|
||||
yield f"data: {json.dumps({'done': True, 'full_message': full_response})}\n\n"
|
||||
|
||||
return StreamingResponse(fallback_generator(), media_type="text/event-stream")
|
||||
|
||||
token_queue = asyncio.Queue()
|
||||
stream_task = regulatory_node.stream_working.remote(payload, token_queue)
|
||||
|
||||
async def event_generator():
|
||||
@@ -207,7 +219,6 @@ async def stream_chat_message(
|
||||
full_response = "抱歉,生成回复时出错。"
|
||||
yield f"data: {json.dumps({'token': full_response})}\n\n"
|
||||
|
||||
# 流结束,存入数据库
|
||||
if full_response:
|
||||
await postgres_database.add_chat_message.remote(
|
||||
chat_id=chat_id,
|
||||
|
||||
@@ -27,7 +27,7 @@ provider_router = APIRouter(prefix="/api/v1/provider", tags=["provider"])
|
||||
class ProviderRegister(BaseModel):
|
||||
"""``POST /provider`` 入参:注册一个模型 Provider 的最小字段集。"""
|
||||
|
||||
provider_type: Literal["openai", "claude", "deepseek"]
|
||||
provider_type: Literal["openai", "claude", "deepseek", "gemini"]
|
||||
provider_title: str
|
||||
provider_url: str
|
||||
provider_apikey: str
|
||||
@@ -72,6 +72,63 @@ async def get_provider_list(
|
||||
return {"provider_list": masked}
|
||||
|
||||
|
||||
@provider_router.post("/test")
|
||||
async def test_provider_connection(
|
||||
provider_register: ProviderRegister,
|
||||
_: TokenData = Depends(Accessor.get_current_user),
|
||||
) -> Dict[str, Any]:
|
||||
"""测试 Provider 连接:按 provider_type 选择对应协议拉取模型列表。"""
|
||||
import httpx
|
||||
|
||||
ptype = provider_register.provider_type
|
||||
url = provider_register.provider_url
|
||||
apikey = provider_register.provider_apikey
|
||||
|
||||
try:
|
||||
async with httpx.AsyncClient(timeout=10.0) as client:
|
||||
if ptype == "claude":
|
||||
endpoint = f"{url}/v1/models"
|
||||
headers = {
|
||||
"x-api-key": apikey,
|
||||
"anthropic-version": "2023-06-01",
|
||||
}
|
||||
response = await client.get(endpoint, headers=headers)
|
||||
if response.status_code == 200:
|
||||
data = response.json()
|
||||
models = [m["id"] for m in data.get("data", [])]
|
||||
return {"success": True, "models": sorted(models), "model_count": len(models)}
|
||||
return {"success": False, "error": f"HTTP {response.status_code}", "models": []}
|
||||
|
||||
elif ptype == "gemini":
|
||||
endpoint = f"{url}/models"
|
||||
params = {"key": apikey}
|
||||
response = await client.get(endpoint, params=params)
|
||||
if response.status_code == 200:
|
||||
data = response.json()
|
||||
models = [m.get("name", "").removeprefix("models/") for m in data.get("models", [])]
|
||||
return {"success": True, "models": sorted(models), "model_count": len(models)}
|
||||
return {"success": False, "error": f"HTTP {response.status_code}", "models": []}
|
||||
|
||||
else:
|
||||
if "/v1" not in url:
|
||||
endpoint = f"{url}/v1/models"
|
||||
else:
|
||||
endpoint = f"{url}/models"
|
||||
headers = {
|
||||
"Authorization": f"Bearer {apikey}",
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
response = await client.get(endpoint, headers=headers)
|
||||
if response.status_code == 200:
|
||||
data = response.json()
|
||||
models = [m["id"] for m in data.get("data", [])]
|
||||
return {"success": True, "models": sorted(models), "model_count": len(models)}
|
||||
return {"success": False, "error": f"HTTP {response.status_code}", "models": []}
|
||||
|
||||
except Exception as e:
|
||||
return {"success": False, "error": str(e), "models": []}
|
||||
|
||||
|
||||
@provider_router.delete("/{provider_title}")
|
||||
async def delete_provider(
|
||||
provider_title: str,
|
||||
|
||||
@@ -39,6 +39,7 @@ class ConsciousnessNode:
|
||||
|
||||
self.logger = get_logger("consciousness_node")
|
||||
self.agent: None | Agent = None
|
||||
self.locale: str = "zh"
|
||||
|
||||
async def create_agent(
|
||||
self,
|
||||
@@ -51,6 +52,7 @@ class ConsciousnessNode:
|
||||
custom_system_prompt: str | None = None,
|
||||
) -> None:
|
||||
system_prompt: str = agent_prompt("consciousness_node", locale=locale, custom_system_prompt=custom_system_prompt)
|
||||
self.locale = locale or "zh"
|
||||
output_type = Union[ForregulatoryNode, ForWorkflow, ForWorkflowEngine]
|
||||
from kilostar.core.global_state_machine.gsm_snapshot import fetch_snapshot
|
||||
|
||||
@@ -74,23 +76,43 @@ class ConsciousnessNode:
|
||||
|
||||
@self.agent.system_prompt
|
||||
async def dynamic_prompt(ctx: RunContext[ConsciousnessNodeDeps]):
|
||||
locale = ctx.deps.locale
|
||||
prompt = system_prompt + "\n\n"
|
||||
prompt += (
|
||||
f"=== 当前任务上下文 ===\n"
|
||||
f"- 当前指令 (Command): {ctx.deps.command}\n"
|
||||
f"- 原始用户命令 (Original Command): {ctx.deps.original_command}\n"
|
||||
)
|
||||
if ctx.deps.available_skills:
|
||||
prompt += "\n=== 当前可用 Skill Individual ===\n"
|
||||
prompt += "你可以直接将以下 Skill Individual 安排进工作流的步骤中(设置 node 为 skill_individual,并将 agent_id 设置为对应 Skill Individual 的真实 agent_id,不要用名称!),作为可调用的工具。\n"
|
||||
for skill in ctx.deps.available_skills:
|
||||
prompt += f"- 真实 agent_id: {skill.get('agent_id')}\n 名称: {skill['name']}\n 描述: {skill['description']}\n"
|
||||
|
||||
if locale == "en":
|
||||
prompt += (
|
||||
f"=== Current Task Context ===\n"
|
||||
f"- Command: {ctx.deps.command}\n"
|
||||
f"- Original User Command: {ctx.deps.original_command}\n"
|
||||
)
|
||||
if ctx.deps.available_skills:
|
||||
prompt += "\n=== Available Skill Individuals ===\n"
|
||||
prompt += "You may assign the following Skill Individuals to workflow steps (set node to skill_individual, and set agent_id to the real agent_id below — never use the name!).\n"
|
||||
for skill in ctx.deps.available_skills:
|
||||
prompt += f"- agent_id: {skill.get('agent_id')}\n Name: {skill['name']}\n Description: {skill['description']}\n"
|
||||
else:
|
||||
prompt += "\n=== IMPORTANT: No Worker Individuals Available ===\n"
|
||||
prompt += "No Worker Individuals are registered. When generating a workflow, you have exactly two options:\n"
|
||||
prompt += "1. Assign the step to consciousness_node itself (set node to consciousness_node, agent_id to null).\n"
|
||||
prompt += "2. If the task truly requires specialized tools, refuse and explain that a Worker must be created first.\n"
|
||||
prompt += "NEVER fabricate non-existent agent_ids!\n"
|
||||
else:
|
||||
prompt += "\n=== 重要:当前无可用 Worker Individual ===\n"
|
||||
prompt += "系统中当前没有注册任何 Worker Individual。在生成工作流时,你有且仅有以下两种选择:\n"
|
||||
prompt += "1. 将步骤分配给 consciousness_node 自己完成(设置 node 为 consciousness_node,agent_id 为 null)。\n"
|
||||
prompt += "2. 如果任务确实需要专用工具或技能才能完成,则拒绝执行并在输出中说明需要先创建对应的 Worker。\n"
|
||||
prompt += "绝对禁止编造不存在的 agent_id!\n"
|
||||
prompt += (
|
||||
f"=== 当前任务上下文 ===\n"
|
||||
f"- 当前指令: {ctx.deps.command}\n"
|
||||
f"- 原始用户命令: {ctx.deps.original_command}\n"
|
||||
)
|
||||
if ctx.deps.available_skills:
|
||||
prompt += "\n=== 当前可用 Skill Individual ===\n"
|
||||
prompt += "你可以直接将以下 Skill Individual 安排进工作流的步骤中(设置 node 为 skill_individual,并将 agent_id 设置为对应 Skill Individual 的真实 agent_id,不要用名称!)。\n"
|
||||
for skill in ctx.deps.available_skills:
|
||||
prompt += f"- 真实 agent_id: {skill.get('agent_id')}\n 名称: {skill['name']}\n 描述: {skill['description']}\n"
|
||||
else:
|
||||
prompt += "\n=== 重要:当前无可用 Worker Individual ===\n"
|
||||
prompt += "系统中当前没有注册任何 Worker Individual。在生成工作流时,你有且仅有以下两种选择:\n"
|
||||
prompt += "1. 将步骤分配给 consciousness_node 自己完成(设置 node 为 consciousness_node,agent_id 为 null)。\n"
|
||||
prompt += "2. 如果任务确实需要专用工具或技能才能完成,则拒绝执行并在输出中说明需要先创建对应的 Worker。\n"
|
||||
prompt += "绝对禁止编造不存在的 agent_id!\n"
|
||||
|
||||
return prompt
|
||||
|
||||
@@ -127,7 +149,6 @@ class ConsciousnessNode:
|
||||
original_command=command, available_skills=available_skills
|
||||
)
|
||||
|
||||
# 通知 SSE 正在生成图结构(pending 队列:节点端写入 → API SSE 读取,单向下行)
|
||||
global_workflow_manager = ray_actor_hook(
|
||||
"global_workflow_manager"
|
||||
).global_workflow_manager
|
||||
@@ -135,7 +156,6 @@ class ConsciousnessNode:
|
||||
trace_id, "正在为您构建并规划工作流任务节点,请稍候..."
|
||||
)
|
||||
|
||||
# 实际构建过程
|
||||
result = await self.working(payload)
|
||||
|
||||
if result and isinstance(result, ForWorkflowEngine):
|
||||
@@ -197,6 +217,7 @@ class ConsciousnessNode:
|
||||
original_command=payload.original_command,
|
||||
command="自主分析并拆解原始命令,生成严密可执行的工作流",
|
||||
available_skills=payload.available_skills,
|
||||
locale=self.locale,
|
||||
)
|
||||
self.logger.debug("ConsciousnessNode: 开始生成工作流 (原生重试开启)")
|
||||
prompt = "根据original_command制定严密的可执行workflow"
|
||||
@@ -207,6 +228,7 @@ class ConsciousnessNode:
|
||||
deps = ConsciousnessNodeDeps(
|
||||
original_command=payload.original_command,
|
||||
command="完成workflow step中分配给意识节点的特定任务或指导",
|
||||
locale=self.locale,
|
||||
)
|
||||
self.logger.debug(
|
||||
"ConsciousnessNode: 开始处理工作流节点任务 (原生重试开启)"
|
||||
@@ -221,6 +243,7 @@ class ConsciousnessNode:
|
||||
deps = ConsciousnessNodeDeps(
|
||||
original_command=payload.original_command,
|
||||
command="对于工作流整体执行结果进行检查,并且生成一份专业的技术性总结报告",
|
||||
locale=self.locale,
|
||||
)
|
||||
self.logger.debug(
|
||||
"ConsciousnessNode: 开始生成技术总结报告 (原生重试开启)"
|
||||
|
||||
@@ -28,7 +28,8 @@ class ConsciousnessNodeDeps(DepsModel):
|
||||
"""ConsciousnessNode 在 pydantic-ai Agent 中使用的依赖:原始指令、当前指令以及可用 Skill 列表。"""
|
||||
original_command: str
|
||||
command: str
|
||||
available_skills: Optional[List[str]] = None
|
||||
available_skills: Optional[List[dict]] = None
|
||||
locale: str = "zh"
|
||||
|
||||
class ConsciousnessNodeInput(RequestModel):
|
||||
"""ConsciousnessNode 各类入参的共同基类,仅用于打 schema 标签。"""
|
||||
|
||||
@@ -30,10 +30,9 @@ from kilostar.utils.i18n import agent_prompt
|
||||
|
||||
@actor_class
|
||||
class RegulatoryNode:
|
||||
"""RegulatoryNode(监管节点):用户请求的入口路由 Actor。
|
||||
"""RegulatoryNode(监管节点):用户请求的直接对话节点。
|
||||
|
||||
负责对消息做意图识别:闲聊 → 直接回 ``ForUser``;复杂任务 → 走
|
||||
``ForConsciousnessNode`` 移交给意识节点;工作流回执 → 转译成对用户的总结回复。
|
||||
负责理解用户需求并提供回复;如果收到工作流执行报告则转化为用户友好的总结。
|
||||
"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
@@ -100,12 +99,9 @@ class RegulatoryNode:
|
||||
f"- 用户名 (User): {ctx.deps.user_name}\n"
|
||||
f"- 当前时间 (Time): {ctx.deps.time}\n"
|
||||
)
|
||||
# 修改 system_prompt 变量
|
||||
prompt += (
|
||||
"\n\n注意:你必须调用且只能调用一个函数(工具)来输出结果。"
|
||||
"如果你想直接回复用户,请调用 ForUser;"
|
||||
"如果你想移交给工作流,请调用 ForConsciousnessNode。"
|
||||
"严禁返回纯文本,必须使用工具格式!"
|
||||
"\n\n注意:请基于上下文信息为用户提供准确、专业的回复。"
|
||||
"如果你有可用工具,可在需要时主动调用。"
|
||||
)
|
||||
if ctx.deps.error_history:
|
||||
prompt += (
|
||||
@@ -130,7 +126,7 @@ class RegulatoryNode:
|
||||
"规则:\n"
|
||||
"1. 直接、详细地回答用户问题,像一个专业且友好的助手。\n"
|
||||
"2. 如果你有可用工具,可以调用工具来辅助回答(如搜索、读文件等)。\n"
|
||||
"3. 不要输出内部思考过程,不要做路由判断,不要提及 ForUser/ForConsciousnessNode 等格式。\n"
|
||||
"3. 不要输出内部思考过程,直接给出回复内容。\n"
|
||||
"4. 回复应当完整、有帮助,避免过于简短。\n"
|
||||
)
|
||||
|
||||
|
||||
@@ -104,7 +104,7 @@ class IndividualDatabase:
|
||||
individual = results.scalar_one_or_none()
|
||||
if not individual:
|
||||
return False
|
||||
session.delete(individual)
|
||||
await session.delete(individual)
|
||||
await session.commit()
|
||||
return True
|
||||
|
||||
|
||||
@@ -90,7 +90,7 @@ class ProviderDatabase:
|
||||
async with self.async_session_maker() as session:
|
||||
provider = await session.get(ProviderModel, provider_id)
|
||||
if provider is not None:
|
||||
session.delete(provider)
|
||||
await session.delete(provider)
|
||||
await session.commit()
|
||||
|
||||
@database_exception
|
||||
|
||||
@@ -78,7 +78,7 @@ class AuthDatabase:
|
||||
user = results.scalar_one_or_none()
|
||||
if user is None:
|
||||
raise UserNotExistError()
|
||||
session.delete(user)
|
||||
await session.delete(user)
|
||||
await session.commit()
|
||||
|
||||
@database_exception
|
||||
@@ -88,7 +88,7 @@ class AuthDatabase:
|
||||
user = await session.get(User, user_id)
|
||||
if user is None:
|
||||
raise UserNotExistError()
|
||||
session.delete(user)
|
||||
await session.delete(user)
|
||||
await session.commit()
|
||||
|
||||
@database_exception
|
||||
|
||||
@@ -223,11 +223,28 @@ class Finalize(BaseNode[WorkflowGraphState, WorkflowDeps, str]):
|
||||
) -> End[str]:
|
||||
ctx.state.final_status = self.status
|
||||
await ctx.deps.update_workflow_status(ctx.state.trace_id, self.status)
|
||||
msg = (
|
||||
"工作流执行完成!"
|
||||
if self.status == WorkflowStatus.COMPLETED.value
|
||||
else "工作流执行失败。"
|
||||
)
|
||||
|
||||
if self.status == WorkflowStatus.COMPLETED.value:
|
||||
summary_parts = []
|
||||
for key, val in ctx.state.blackboard.items():
|
||||
text = str(val)[:200]
|
||||
summary_parts.append(f"• {key}: {text}")
|
||||
summary = "\n".join(summary_parts) if summary_parts else ""
|
||||
msg = f"工作流执行完成!\n{summary}" if summary else "工作流执行完成!"
|
||||
else:
|
||||
failed_logs = [
|
||||
entry for entry in ctx.state.logs
|
||||
if any(
|
||||
isinstance(v, (list, tuple)) and len(v) >= 2 and v[1] == "failed"
|
||||
for v in (entry.values() if isinstance(entry, dict) else [])
|
||||
)
|
||||
]
|
||||
msg = "工作流执行失败。"
|
||||
if failed_logs:
|
||||
last = list(failed_logs[-1].values())[0]
|
||||
if isinstance(last, (list, tuple)) and len(last) >= 3:
|
||||
msg += f"\n失败原因: {last[2][:300]}"
|
||||
|
||||
await ctx.deps.put_pending(ctx.state.trace_id, msg)
|
||||
return End(self.status)
|
||||
|
||||
@@ -295,9 +312,13 @@ async def _execute_step(
|
||||
state.logs[-1][str(state.current_step_index)] = [
|
||||
str(datetime.datetime.now()),
|
||||
"completed",
|
||||
f"成功: {step_data.get('action', '')}",
|
||||
output_text,
|
||||
]
|
||||
await _persist_context(ctx, status=WorkflowStatus.RUNNING.value)
|
||||
await ctx.deps.put_pending(
|
||||
state.trace_id,
|
||||
f"✅ 步骤 {state.current_step_index + 1} ({step_data.get('name', '')}) 完成:\n{output_text[:500]}",
|
||||
)
|
||||
|
||||
logic_gate = step_data.get("logic_gate") or {}
|
||||
if logic_gate.get("if_pass") == "exit":
|
||||
@@ -314,6 +335,10 @@ async def _execute_step(
|
||||
"failed",
|
||||
output_text,
|
||||
]
|
||||
await ctx.deps.put_pending(
|
||||
state.trace_id,
|
||||
f"❌ 步骤 {state.current_step_index + 1} ({step_data.get('name', '')}) 失败:\n{output_text[:300]}",
|
||||
)
|
||||
logic_gate = step_data.get("logic_gate") or {}
|
||||
fail_target = logic_gate.get("if_fail")
|
||||
if fail_target and "jump_to_step_" in fail_target:
|
||||
|
||||
+15
-15
@@ -36,24 +36,24 @@ _DEFAULT_LOCALE: str = get_settings().kilostar_lang
|
||||
_PROMPTS: Dict[str, Dict[str, str]] = {
|
||||
"regulatory_node": {
|
||||
"zh": (
|
||||
"你叫kilostar,是一个多智能体AI助手系统中的【监控节点 (regulatory Node)】。\n"
|
||||
"你是系统的'前台接待'和'大脑皮层',负责接收用户的初始请求或工作流的最终报告。\n"
|
||||
"你的核心职责是进行【意图识别与路由】。请仔细阅读用户的请求:\n"
|
||||
"1. 如果用户只是进行简单的问候、闲聊或查询非常基础的信息,请直接生成友好的回复,使用 ForUser 格式。\n"
|
||||
"2. 如果用户提出的是复杂任务(如需要编写代码、多步骤规划、数据处理等),请务必将其判定为需要工作流处理的任务,"
|
||||
" 并使用 ForConsciousnessNode 格式将其移交意识节点处理。\n"
|
||||
"3. 如果你收到的是 TerminationMessage(代表工作流已完成并生成了报告),请将报告内容转化为友好的面向用户的回复,使用 ForUser 格式。\n"
|
||||
"请保持冷静、专业,并严格遵循上述路由规则。"
|
||||
"你叫kilostar,是一个多智能体AI助手系统中的【监管节点 (Regulatory Node)】。\n"
|
||||
"你是系统中直接面向用户的对话节点,负责理解用户需求并提供高质量的回复。\n\n"
|
||||
"你的核心职责:\n"
|
||||
"1. 准确理解用户的意图,提供专业、友好且有帮助的回复。\n"
|
||||
"2. 如果你有可用工具,可以主动调用工具来辅助回答(如搜索、文件操作等)。\n"
|
||||
"3. 如果你收到工作流的执行报告,请将其转化为面向用户的清晰总结。\n"
|
||||
"4. 保持回复简洁、有结构,避免冗余信息。\n"
|
||||
"请保持专业、友好的沟通风格。"
|
||||
),
|
||||
"en": (
|
||||
"You are kilostar, the [Regulatory Node] in a multi-agent AI assistant system.\n"
|
||||
"You are the system's 'front desk' and 'cerebral cortex', responsible for receiving user requests and final workflow reports.\n"
|
||||
"Your core duty is [intent recognition and routing]. Please read the user's request carefully:\n"
|
||||
"1. If the user is simply greeting, chatting, or asking very basic questions, generate a friendly reply directly in the ForUser format.\n"
|
||||
"2. If the user presents a complex task (e.g., writing code, multi-step planning, data processing), you must classify it as a workflow-requiring task "
|
||||
" and hand it over to the Consciousness Node using the ForConsciousnessNode format.\n"
|
||||
"3. If you receive a TerminationMessage (indicating the workflow is complete and a report has been generated), convert the report into a user-friendly reply in the ForUser format.\n"
|
||||
"Please remain calm, professional, and strictly follow the routing rules above."
|
||||
"You are the user-facing conversational node, responsible for understanding user needs and providing high-quality responses.\n\n"
|
||||
"Your core responsibilities:\n"
|
||||
"1. Accurately understand user intent and provide professional, friendly, and helpful replies.\n"
|
||||
"2. If tools are available, proactively use them to assist your responses (e.g., search, file operations).\n"
|
||||
"3. If you receive a workflow execution report, convert it into a clear user-facing summary.\n"
|
||||
"4. Keep responses concise, well-structured, and free of redundancy.\n"
|
||||
"Maintain a professional and friendly communication style."
|
||||
),
|
||||
},
|
||||
"consciousness_node": {
|
||||
|
||||
@@ -41,7 +41,7 @@ class OrdinaryIndividual(BaseIndividual):
|
||||
self.agent.retries = 3
|
||||
try:
|
||||
result = await self.agent.run(f"请执行以下任务:\n{task_event}", deps=deps)
|
||||
return {"output": result.data.output}
|
||||
return {"output": result.output.output}
|
||||
except Exception as e:
|
||||
logger.exception(f"OrdinaryIndividual {self.agent_id} 执行失败: {e}")
|
||||
raise
|
||||
|
||||
@@ -123,7 +123,7 @@ class SkillIndividual(BaseIndividual):
|
||||
deps=deps,
|
||||
tools=tools if tools else None,
|
||||
)
|
||||
return {"output": result.data.output}
|
||||
return {"output": result.output.output}
|
||||
except Exception as e:
|
||||
logger.exception(f"SkillIndividual {self.agent_id} 执行失败: {e}")
|
||||
raise
|
||||
|
||||
@@ -41,7 +41,7 @@ class SpecialIndividual(BaseIndividual):
|
||||
self.agent.retries = 3
|
||||
try:
|
||||
result = await self.agent.run(f"请执行以下任务:\n{task_event}", deps=deps)
|
||||
return {"output": result.data.output}
|
||||
return {"output": result.output.output}
|
||||
except Exception as e:
|
||||
logger.exception(f"SpecialIndividual {self.agent_id} 执行失败: {e}")
|
||||
raise
|
||||
|
||||
Reference in New Issue
Block a user