Files
zhenxun_bot/zhenxun/services/ai/flow/team/task_tools.py
T
80fc5b86a7 ✨ feat!(llm): 重构并升级大语言模型服务为全新 AI 智能体框架 (#2146)
* ✨ feat!(llm): 重构并升级大语言模型服务为全新 AI 智能体框架

- 【重构】将原 services/llm 重构并迁移至全新的 services/ai 架构,提供向下兼容垫片
- 【新增】引入 Agent、Team、Workflow 三大智能体与工作流编排范式
- 【新增】引入基于 RAG 的长期向量记忆与中期槽位记忆系统
- 【新增】引入基于 Docker 的安全代码执行沙箱环境
- 【新增】支持 MCP 协议,允许动态管理和调用 MCP 服务
- 【新增】引入输入输出安全合规护栏与自愈反思机制
- 【优化】重构并优化多厂商 API 适配器 (Gemini, OpenAI, DeepSeek, GLM 等)
- 【优化】优化日志脱敏与 Token 预估机制
- 【移除】移除旧版 llm default 和 llm reset-key 命令,新增 llm mcp 管理命令

* 🔧 chore(deps): 更新项目依赖与配置

- 添加 mcp、jieba 和 aiodocker 依赖到配置文件及 requirements.txt
- 在 pyright 配置中设置 reportMissingImports 为 none
- 调整 .gitignore 中 resources 目录的忽略规则

* ♻️ refactor(tools): 重构工具终止机制并清理知识库日志输出

- 统一使用 `context.state["__end_run__"]` 替代 `EndRunResult` 控制任务结束
- 移除文件系统和向量知识库检索工具中 `ToolResult` 的 `.with_log` 调用
- 调整指令处理器(Directive)的返回值为 `tool_res.output`
- 修复部分类型检查警告并优化联合类型判断语法

* ♻️ refactor(tools): 重构工具副作用指令与控制流熔断机制

- 引入 `DirectivePayload` 及 `ToolResult` 的子类以结构化表达工具副作用
- 移除通过 `context.state` 传递魔术变量的隐式控制流设计
- 重构 `DirectiveManager` 处理器接口,直接在处理器中修改 `AgentState` 并构建 `AgentRunResult`
- 在 `StandardAgentExecutor` 中统一通过 `directive_manager` 调度工具返回的副作用指令
- 补全 `MessageBuilder` 中部分核心方法的文档注释

* 🐛 fix(sandbox): 修复 Docker 沙箱容器状态检测与会话清理逻辑

-【修复】修正 `is_alive` 中直接读取私有属性的问题,改用 `show()` 返回值
-【修复】解决 `execute_code` 中缓存的执行器与当前会话不一致的问题
-【优化】在清理工作区前增加容器存活检测,避免向已死容器发送请求
-【优化】创建容器时增加运行状态校验,若已停止则自动从缓存中移除并重建
-【优化】优化容器销毁和清理逻辑,静默处理容器不存在 (404) 的异常

* 📝 docs(core): 补充核心模块初始化方法的文档注释

* 🚨 auto fix by pre-commit hooks

---------

Co-authored-by: webjoin111 <455457521@qq.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2026-07-03 08:53:56 +08:00

197 lines
7.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from typing import Annotated, Any
from pydantic import Field
from zhenxun.services.ai.flow.base import BaseRunnable
from zhenxun.services.ai.run.context import RunContext
from zhenxun.services.ai.tools.core.decorators import tool
from zhenxun.services.ai.tools.core.toolkit import BaseToolkit
from zhenxun.services.ai.tools.models import ToolResult
from zhenxun.services.log import logger
from .models import TaskBoardState, TaskNodeStatus
class TaskPlanningToolkit(BaseToolkit):
"""
任务规划工具箱 (Planner Toolkit)。
大模型专用的黑板操作工具。大模型被剥夺了执行权,仅能拆解、指派和总结任务。
"""
default_prefix = ""
default_instructions = """<instructions>
## 🛠️ 任务规划工作流指南
你现在的角色是**项目经理 (Planner)**。你的唯一职责是拆解任务、分配人员并监控看板状态,**系统底层会自动拉起专家执行任务**。
1. **规划**:使用 `create_task` 拆解任务,设定 `assignee`(专家名称)和 `depends_on`(依赖的其它任务ID)。
2. **等待与监控**:每次你创建或更新任务后,请立刻停止工具调用,系统引擎会自动并发执行 pending 任务并再次唤醒你。
3. **🩹 智能自愈与重试**:如果你被唤醒后,看到看板上有任务处于 `failed` 状态,
请仔细阅读失败结果 (result)。你可以通过 `update_task_status` 将该任务的状态重新修改为 `pending`
以触发重新执行(可以附带修改建议在 result 里),或者创建新任务替代它。
4. **终结**:当你确认所有目标已达成时,调用 `mark_all_complete` 附上最终总结,正式结束整个流水线。
⚠️ 警告:你没有任何执行具体业务代码或查询的工具,你只能操作任务看板!
</instructions>""" # noqa: E501
def __init__(self, members: list[BaseRunnable], **kwargs):
super().__init__(**kwargs)
self.members = members
def _get_board(self, context: RunContext) -> TaskBoardState:
"""从运行上下文中安全的获取或初始化任务看板状态"""
if "__task_board__" not in context.session.shared_state:
context.session.shared_state["__task_board__"] = TaskBoardState()
return context.session.shared_state["__task_board__"]
@tool(description="创建一个新任务并加入看板。")
async def create_task(
self,
title: Annotated[str, Field(description="任务的简短、可行动的标题")],
description: Annotated[
str, Field(description="详细的任务说明,告诉执行者需要做什么以及期望的产出")
],
assignee: Annotated[
str,
Field(
description=(
"负责执行此任务的专家名称,必须完全匹配 <team_members> "
"中提供的 id,严禁捏造"
)
),
],
context: RunContext,
depends_on: Annotated[
list[str],
Field(
description=(
"该任务依赖的前置任务的【标题(title)】列表"
"(因同一回合创建时未知ID,请务必使用前置任务的 title 作为依赖)。"
"无依赖则必须传入空数组 []"
),
),
],
metadata: Annotated[
dict[str, Any],
Field(
description="可选的附加字典,用于向执行专家传递额外的结构化约束或参数"
),
] = {},
) -> ToolResult:
board = self._get_board(context)
valid_member_names = [m.name for m in self.members]
if assignee not in valid_member_names:
return ToolResult(
output=(
f"❌ 创建失败:未找到名为 '{assignee}' 的专家。"
f"可用专家: {valid_member_names}"
)
).as_error()
task = board.create_task(
title=title,
description=description,
assignee=assignee,
dependencies=depends_on,
metadata=metadata,
)
logger.debug(f" 🆕 [新建任务] `{task.title}` -> 👨💼{task.assignee}")
board_str = board.render_board_to_string()
return ToolResult(
output=(
f"✅ 任务创建成功!任务 ID: [{task.id}],"
f"状态: {task.status.value}\n\n{board_str}"
)
)
@tool(
description=(
"手动强制更新任务的状态(仅在特殊情况下使用,"
"因为 execute_task 会自动更新状态)。"
)
)
async def update_task_status(
self,
task_id: Annotated[str, Field(description="要更新的任务的唯一 ID")],
status: Annotated[
TaskNodeStatus,
Field(
description=(
"新的任务状态,支持: pending(用于重试), completed, failed 等"
)
),
],
context: RunContext,
result: Annotated[
str,
Field(
description=(
"提供结果、失败原因,或在设为 pending 重试时给执行专家的建议"
)
),
] = "",
) -> ToolResult:
board = self._get_board(context)
if status == TaskNodeStatus.in_progress:
return ToolResult(
output=(
"❌ 权限拒绝:你不能手动将任务状态设置为 in_progress。"
"该状态由底层执行引擎自动管理。如果你想让任务重新执行,"
"请将其设置为 pending。"
)
).as_error()
updated = board.update_task_status(task_id, status, result if result else None)
if not updated:
return ToolResult(output=f"❌ 找不到 ID 为 '{task_id}' 的任务。").as_error()
task_obj = board.get_task(task_id)
task_title = task_obj.title if task_obj else "Unknown"
logger.debug(f" 🔄 [任务状态变更] `{task_title}` -> {status.value}")
if task_obj and task_obj.status != status:
board_str = board.render_board_to_string()
return ToolResult(
output=(
"❌ 状态更新失败(触发底层状态机防呆回滚)!\n"
f"你尝试将 [{task_id}] 强制设置为 {status.value},"
f"但系统计算依赖图后将其重置为了 {task_obj.status.value}。\n"
"💡 原因分析:它的前置依赖(depends_on)可能尚未 COMPLETED,"
"或者你填错了依赖项的名称/ID导致系统无法追踪。\n\n"
f"{board_str}"
)
).as_error()
board_str = board.render_board_to_string()
return ToolResult(
output=f"✅ 任务 [{task_id}] 已更新为 {status.value}。\n\n{board_str}"
)
@tool(
description=(
"声明整体目标已完成。在调用此工具后,"
"大模型将被立刻中断并直接将 summary 返回给用户。"
)
)
async def mark_all_complete(
self,
summary: Annotated[
str,
Field(
description=(
"流程的最终战报。⚠️ 必须在总结中完整包含各专家产出的"
"核心交付物原文(如生成的故事、最终翻译内容等),绝对不能只说“已完成”!"
)
),
],
context: RunContext,
) -> ToolResult:
from zhenxun.services.ai.tools.models import EndRunResult
board = self._get_board(context)
board.is_goal_complete = True
board.final_summary = summary
return EndRunResult(output=summary)