refactor: core剥离SDLC能力——GENERAL_TOOLS通用内核+PipelineAbility可插拔能力包注册表+load_agent_config按pipeline_id动态挂载
This commit is contained in:
parent
63760ff186
commit
9635825164
@ -20,11 +20,22 @@ from .agent_config import (
|
|||||||
MemoryConfig,
|
MemoryConfig,
|
||||||
SkillConfig,
|
SkillConfig,
|
||||||
ToolDefinition,
|
ToolDefinition,
|
||||||
|
GENERAL_TOOLS,
|
||||||
|
DEFAULT_AGENT_CONFIG,
|
||||||
|
DEFAULT_ABILITY_ID,
|
||||||
SDLC_DEFAULT_CONFIG,
|
SDLC_DEFAULT_CONFIG,
|
||||||
SDLC_DEFAULT_TOOLS,
|
SDLC_DEFAULT_TOOLS,
|
||||||
load_agent_config,
|
load_agent_config,
|
||||||
save_agent_config,
|
save_agent_config,
|
||||||
)
|
)
|
||||||
|
from .ability import (
|
||||||
|
PipelineAbility,
|
||||||
|
register_ability,
|
||||||
|
get_ability,
|
||||||
|
list_abilities,
|
||||||
|
get_ability_tools,
|
||||||
|
get_ability_handler,
|
||||||
|
)
|
||||||
from .tool_registry import (
|
from .tool_registry import (
|
||||||
ToolRegistry,
|
ToolRegistry,
|
||||||
get_tool_registry,
|
get_tool_registry,
|
||||||
|
|||||||
73
pipeline_core/ability.py
Normal file
73
pipeline_core/ability.py
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
"""
|
||||||
|
pipeline-core: PipelineAbility — 产线能力包注册表
|
||||||
|
|
||||||
|
可插拔产线能力:core 只提供通用 agent 内核(通用工具 + 通用心智),
|
||||||
|
每个产线(SDLC 开发、未来 A/B 产线)以「能力包」形式独立注册,
|
||||||
|
包含该产线专属的工具定义、prompt 片段、工具 handler。
|
||||||
|
|
||||||
|
AgentExecutor 按 pipeline_id 从注册表动态挂载能力 → 多实例能力各异。
|
||||||
|
|
||||||
|
依赖方向:
|
||||||
|
pipeline-core (本文件,纯定义 + 注册表,不依赖任何产线)
|
||||||
|
pipeline-service (sdlc_ability.py 注册 SDLC 能力,依赖 core + 底层函数)
|
||||||
|
"""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
from dataclasses import dataclass, field
|
||||||
|
from typing import Callable, Dict, List, Optional
|
||||||
|
|
||||||
|
logger = logging.getLogger("pipeline.ability")
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class PipelineAbility:
|
||||||
|
"""产线能力包:一个产线 = 一组工具 + 一段 prompt + 一组 handler。
|
||||||
|
|
||||||
|
handlers 签名统一为:async def handler(sor, params, ctx) -> str
|
||||||
|
- sor: 已打开的 pipeline DB context(由 AgentExecutor 统一管理)
|
||||||
|
- params: 工具参数 dict
|
||||||
|
- ctx: {"project_id", "user_id", "workspace_dir", "model_name", "config"}
|
||||||
|
"""
|
||||||
|
pipeline_id: str # 产线标识(对应 pipelines 表 id)
|
||||||
|
name: str = "" # 产线名称
|
||||||
|
tools: List = field(default_factory=list) # [ToolDefinition]
|
||||||
|
system_prompt: str = "" # 产线专属 prompt 片段(追加到通用心智后)
|
||||||
|
handlers: Dict[str, Callable] = field(default_factory=dict) # {tool_name: handler}
|
||||||
|
|
||||||
|
|
||||||
|
# ── 全局注册表 ──
|
||||||
|
|
||||||
|
_abilities: Dict[str, PipelineAbility] = {}
|
||||||
|
|
||||||
|
|
||||||
|
def register_ability(ability: PipelineAbility):
|
||||||
|
"""注册一个产线能力包(幂等:重复注册覆盖)。"""
|
||||||
|
_abilities[ability.pipeline_id] = ability
|
||||||
|
logger.info(f"PipelineAbility registered: {ability.pipeline_id} "
|
||||||
|
f"({len(ability.tools)} tools, {len(ability.handlers)} handlers)")
|
||||||
|
|
||||||
|
|
||||||
|
def get_ability(pipeline_id: str) -> Optional[PipelineAbility]:
|
||||||
|
"""按 pipeline_id 取能力包。"""
|
||||||
|
if not pipeline_id:
|
||||||
|
return None
|
||||||
|
return _abilities.get(pipeline_id)
|
||||||
|
|
||||||
|
|
||||||
|
def list_abilities() -> List[PipelineAbility]:
|
||||||
|
"""列出所有已注册能力包。"""
|
||||||
|
return list(_abilities.values())
|
||||||
|
|
||||||
|
|
||||||
|
def get_ability_tools(pipeline_id: str) -> List:
|
||||||
|
"""取某产线的专属工具定义列表。"""
|
||||||
|
a = get_ability(pipeline_id)
|
||||||
|
return a.tools if a else []
|
||||||
|
|
||||||
|
|
||||||
|
def get_ability_handler(pipeline_id: str, tool_name: str) -> Optional[Callable]:
|
||||||
|
"""取某产线某工具的 handler。"""
|
||||||
|
a = get_ability(pipeline_id)
|
||||||
|
if not a:
|
||||||
|
return None
|
||||||
|
return a.handlers.get(tool_name)
|
||||||
@ -175,10 +175,15 @@ class AgentConfig:
|
|||||||
|
|
||||||
|
|
||||||
# ═══════════════════════════════════════════════════════════
|
# ═══════════════════════════════════════════════════════════
|
||||||
# SDLC 默认配置
|
# 通用 agent 内核(产线无关)
|
||||||
|
#
|
||||||
|
# GENERAL_TOOLS:任何产线 agent 都具备的基础能力
|
||||||
|
# (项目管理/终端/文件/搜索/会话/规划/澄清/委派)。
|
||||||
|
# 产线专属能力(如 SDLC 的 create_task/diagnose_project)以「能力包」形式
|
||||||
|
# 独立注册,见 pipeline_service.sdlc_ability,通过 PipelineAbility 挂载。
|
||||||
# ═══════════════════════════════════════════════════════════
|
# ═══════════════════════════════════════════════════════════
|
||||||
|
|
||||||
SDLC_DEFAULT_TOOLS = [
|
GENERAL_TOOLS = [
|
||||||
ToolDefinition(
|
ToolDefinition(
|
||||||
name="switch_project",
|
name="switch_project",
|
||||||
description="切换到指定项目。用户说「切换到XXX」时调用。",
|
description="切换到指定项目。用户说「切换到XXX」时调用。",
|
||||||
@ -191,82 +196,6 @@ SDLC_DEFAULT_TOOLS = [
|
|||||||
parameters={"name": "项目名称", "description": "项目描述"},
|
parameters={"name": "项目名称", "description": "项目描述"},
|
||||||
category="project",
|
category="project",
|
||||||
),
|
),
|
||||||
ToolDefinition(
|
|
||||||
name="create_task",
|
|
||||||
description="创建开发任务,分配给角色agent执行",
|
|
||||||
parameters={
|
|
||||||
"title": "任务标题",
|
|
||||||
"role": "目标角色(requirement/design/develop/test/deploy)",
|
|
||||||
"description": "任务详细描述",
|
|
||||||
},
|
|
||||||
category="task",
|
|
||||||
),
|
|
||||||
ToolDefinition(
|
|
||||||
name="list_tasks",
|
|
||||||
description="查看当前项目的任务列表",
|
|
||||||
parameters={"role": "按角色筛选(可选)", "state": "按状态筛选(可选)"},
|
|
||||||
category="task",
|
|
||||||
),
|
|
||||||
ToolDefinition(
|
|
||||||
name="task_detail",
|
|
||||||
description="查看任务详情",
|
|
||||||
parameters={"task_id": "任务ID"},
|
|
||||||
category="task",
|
|
||||||
),
|
|
||||||
ToolDefinition(
|
|
||||||
name="start_agents",
|
|
||||||
description="启动角色agent执行已提交的任务",
|
|
||||||
parameters={},
|
|
||||||
category="agent",
|
|
||||||
),
|
|
||||||
ToolDefinition(
|
|
||||||
name="diagnose_project",
|
|
||||||
description="诊断项目:汇总卡点/失败/问题/运行中任务",
|
|
||||||
parameters={},
|
|
||||||
category="agent",
|
|
||||||
),
|
|
||||||
ToolDefinition(
|
|
||||||
name="list_deliverables",
|
|
||||||
description="查看交付件列表",
|
|
||||||
parameters={"role": "按角色筛选(可选)"},
|
|
||||||
category="task",
|
|
||||||
),
|
|
||||||
ToolDefinition(
|
|
||||||
name="view_deliverable",
|
|
||||||
description="查看交付件详细内容",
|
|
||||||
parameters={"deliverable_id": "交付件ID"},
|
|
||||||
category="task",
|
|
||||||
),
|
|
||||||
ToolDefinition(
|
|
||||||
name="list_questions",
|
|
||||||
description="查看待回答的问题",
|
|
||||||
parameters={},
|
|
||||||
category="agent",
|
|
||||||
),
|
|
||||||
ToolDefinition(
|
|
||||||
name="answer_question",
|
|
||||||
description="回答agent提出的问题",
|
|
||||||
parameters={"question_id": "问题ID", "answer": "回答内容"},
|
|
||||||
category="agent",
|
|
||||||
),
|
|
||||||
ToolDefinition(
|
|
||||||
name="add_repo",
|
|
||||||
description="添加项目关联的Git仓库",
|
|
||||||
parameters={"repo_url": "仓库URL", "repo_name": "仓库名称", "default_branch": "默认分支(默认main)"},
|
|
||||||
category="repo",
|
|
||||||
),
|
|
||||||
ToolDefinition(
|
|
||||||
name="list_repos",
|
|
||||||
description="查看项目关联的仓库列表",
|
|
||||||
parameters={},
|
|
||||||
category="repo",
|
|
||||||
),
|
|
||||||
ToolDefinition(
|
|
||||||
name="clone_repo",
|
|
||||||
description="克隆项目关联的仓库到工作空间repos/下(不传url则clone全部,传则clone指定)",
|
|
||||||
parameters={"repo_url": "仓库URL(可选)"},
|
|
||||||
category="repo",
|
|
||||||
),
|
|
||||||
ToolDefinition(
|
ToolDefinition(
|
||||||
name="run_command",
|
name="run_command",
|
||||||
description="在工作空间中执行shell命令",
|
description="在工作空间中执行shell命令",
|
||||||
@ -274,18 +203,6 @@ SDLC_DEFAULT_TOOLS = [
|
|||||||
category="shell",
|
category="shell",
|
||||||
requires_confirmation=True,
|
requires_confirmation=True,
|
||||||
),
|
),
|
||||||
ToolDefinition(
|
|
||||||
name="check_progress",
|
|
||||||
description="查看项目整体进展",
|
|
||||||
parameters={},
|
|
||||||
category="agent",
|
|
||||||
),
|
|
||||||
ToolDefinition(
|
|
||||||
name="add_bug",
|
|
||||||
description="提交Bug",
|
|
||||||
parameters={"title": "Bug标题", "description": "描述", "severity": "严重程度"},
|
|
||||||
category="task",
|
|
||||||
),
|
|
||||||
# ── v2 新增 ──
|
# ── v2 新增 ──
|
||||||
ToolDefinition(
|
ToolDefinition(
|
||||||
name="ask_user",
|
name="ask_user",
|
||||||
@ -339,36 +256,26 @@ SDLC_DEFAULT_TOOLS = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
SDLC_DEFAULT_CONFIG = AgentConfig(
|
DEFAULT_AGENT_CONFIG = AgentConfig(
|
||||||
model_name="deepseek-v4-pro",
|
model_name="deepseek-v4-pro",
|
||||||
temperature=0.4,
|
temperature=0.4,
|
||||||
max_turns=30,
|
max_turns=30,
|
||||||
system_prompt="""你是 SDLC 开发产线的驾驶舱 agent。你的职责:在自动化项目实施过程中,主动发现问题、定位根因、用工具解决问题,推动项目前进。
|
system_prompt="""你是一个通用 agent,具备通用推理与判断能力,并根据当前产线配备了对应的工具。像一名有经验的负责人那样思考:先理解意图,再拆解问题,判断自己能否解决,必要时澄清或诚实说明。
|
||||||
|
|
||||||
## 你的身份
|
|
||||||
你是一个具备通用推理与判断能力的 agent,在此之上额外配备了项目实施工具。像一名有经验的工程负责人那样思考:先理解意图,再拆解问题,判断自己能否解决,必要时澄清或诚实说明。
|
|
||||||
|
|
||||||
## 工作原则
|
## 工作原则
|
||||||
1. 先理解用户意图,再决定行动。意图模糊时用 ask_user 澄清,不要臆测。
|
1. 先理解用户意图,再决定行动。意图模糊时用 ask_user 澄清,不要臆测。
|
||||||
2. 用工具是手段,不是目的。工具能解决就用工具,用工具只是为了把事做成。
|
2. 用工具是手段,不是目的。工具能解决就用工具,用工具只是为了把事做成。
|
||||||
3. 能力自省与诚实降级:如果用户的请求超出你的工具能力(例如"重做已完成任务",但你没有重置任务状态的工具),不要硬套一个不相关的工具。你必须:
|
3. 能力自省与诚实降级:如果用户的请求超出你的工具能力,不要硬套一个不相关的工具。你必须:
|
||||||
a) 明确告诉用户:你做不到、缺什么能力、为什么;
|
a) 明确告诉用户:你做不到、缺什么能力、为什么;
|
||||||
b) 给出你能做到的替代方案(如用 create_task 新建重做任务);
|
b) 给出你能做到的替代方案;
|
||||||
c) 必要时 ask_user 让用户拍板。
|
c) 必要时 ask_user 让用户拍板。
|
||||||
4. 发现项目卡点(任务卡死、审核超时、失败、僵尸 claimed_by、等待无响应)时,主动定位根因并推动修复,而不是只列一张诊断清单。
|
4. 发现异常/卡点时,主动定位根因并推动解决,而不是只列清单。
|
||||||
5. 每轮输出 tool_call / reply / ask_user 三者之一,根据实际情况选择,没有任何强制。
|
5. 每轮输出 tool_call / reply / ask_user 三者之一,根据实际情况选择,没有任何强制。
|
||||||
|
|
||||||
## 典型场景(帮助判断何时用哪个工具,非强制)
|
|
||||||
- 用户提出新的开发需求/功能("实现XX""XX系统要做XX")→ 通常用 create_task 创建任务(可按 requirement→design→develop 拆分),再 start_agents 启动。
|
|
||||||
- 用户问进展/状态 → list_tasks / diagnose_project / check_progress。
|
|
||||||
- 用户问待回答问题 → list_questions / answer_question。
|
|
||||||
- 用户报告异常/故障 → 先用 run_command / diagnose_project 定位根因,再决定修复动作。
|
|
||||||
- 用户要求做你工具能力之外的事 → 按"能力自省"原则诚实说明 + 给替代方案 + 必要时 ask_user。
|
|
||||||
|
|
||||||
## 工具集
|
## 工具集
|
||||||
{tools_description}
|
{tools_description}
|
||||||
""",
|
""",
|
||||||
tools=SDLC_DEFAULT_TOOLS,
|
tools=GENERAL_TOOLS,
|
||||||
session_isolation="project",
|
session_isolation="project",
|
||||||
history_limit=50,
|
history_limit=50,
|
||||||
compression=CompressionConfig(enabled=True, threshold=0.50, target_ratio=0.20, keep_recent=6),
|
compression=CompressionConfig(enabled=True, threshold=0.50, target_ratio=0.20, keep_recent=6),
|
||||||
@ -377,18 +284,56 @@ SDLC_DEFAULT_CONFIG = AgentConfig(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# 兼容别名(过渡期):SDLC 能力已迁移到 pipeline_service.sdlc_ability 能力包,
|
||||||
|
# 此处保留别名以免破坏既有引用,后续可删除。
|
||||||
|
SDLC_DEFAULT_TOOLS = GENERAL_TOOLS
|
||||||
|
SDLC_DEFAULT_CONFIG = DEFAULT_AGENT_CONFIG
|
||||||
|
|
||||||
|
# 默认产线:pipeline_id 为空时回退的能力包(对应 pipelines 表的「通用软件开发产线」)
|
||||||
|
DEFAULT_ABILITY_ID = "sdlc_general"
|
||||||
|
|
||||||
|
|
||||||
# ═══════════════════════════════════════════════════════════
|
# ═══════════════════════════════════════════════════════════
|
||||||
# 配置加载器
|
# 配置加载器
|
||||||
# ═══════════════════════════════════════════════════════════
|
# ═══════════════════════════════════════════════════════════
|
||||||
|
|
||||||
async def load_agent_config(pipeline_id: str = None, project_id: str = None) -> AgentConfig:
|
async def load_agent_config(pipeline_id: str = None, project_id: str = None) -> AgentConfig:
|
||||||
"""加载产线的 Agent 配置。
|
"""加载产线的 Agent 配置(通用内核 + 可插拔产线能力)。
|
||||||
|
|
||||||
优先级:
|
组装顺序:
|
||||||
1. 项目级 sd_org_settings.agent_config(sd_org_settings 以 org_id 为键,由项目解析 org_id)
|
1. 基础 = DEFAULT_AGENT_CONFIG(通用心智 + GENERAL_TOOLS)
|
||||||
2. 产线级 pipelines.agent_config
|
2. 产线能力 = get_ability(pipeline_id) 动态挂载(工具 + prompt 片段)
|
||||||
3. SDLC_DEFAULT_CONFIG
|
—— pipeline_id 为空时回退 DEFAULT_ABILITY_ID(默认产线)
|
||||||
|
3. DB 覆盖:sd_org_settings.agent_config / pipelines.agent_config
|
||||||
"""
|
"""
|
||||||
|
from .ability import get_ability
|
||||||
|
|
||||||
|
# 1. 基础通用配置
|
||||||
|
base = DEFAULT_AGENT_CONFIG
|
||||||
|
|
||||||
|
# 2. 产线能力挂载
|
||||||
|
ability = get_ability(pipeline_id) if pipeline_id else get_ability(DEFAULT_ABILITY_ID)
|
||||||
|
tools = list(GENERAL_TOOLS)
|
||||||
|
prompt = base.system_prompt
|
||||||
|
if ability:
|
||||||
|
tools = _merge_tools(ability.tools, tools)
|
||||||
|
if ability.system_prompt:
|
||||||
|
prompt += "\n\n## 产线专属能力\n" + ability.system_prompt
|
||||||
|
|
||||||
|
cfg = AgentConfig(
|
||||||
|
model_name=base.model_name,
|
||||||
|
temperature=base.temperature,
|
||||||
|
max_turns=base.max_turns,
|
||||||
|
system_prompt=prompt,
|
||||||
|
tools=tools,
|
||||||
|
session_isolation=base.session_isolation,
|
||||||
|
history_limit=base.history_limit,
|
||||||
|
compression=base.compression,
|
||||||
|
memory=base.memory,
|
||||||
|
skills=base.skills,
|
||||||
|
)
|
||||||
|
|
||||||
|
# 3. DB 覆盖(项目级 / 产线级)
|
||||||
try:
|
try:
|
||||||
from sqlor.dbpools import DBPools
|
from sqlor.dbpools import DBPools
|
||||||
|
|
||||||
@ -411,7 +356,7 @@ async def load_agent_config(pipeline_id: str = None, project_id: str = None) ->
|
|||||||
if raw:
|
if raw:
|
||||||
try:
|
try:
|
||||||
data = json.loads(raw) if isinstance(raw, str) else raw
|
data = json.loads(raw) if isinstance(raw, str) else raw
|
||||||
data["tools"] = _merge_tools(data.get("tools", []), SDLC_DEFAULT_TOOLS)
|
data["tools"] = _merge_tools(data.get("tools", []), tools)
|
||||||
return AgentConfig.from_dict(data)
|
return AgentConfig.from_dict(data)
|
||||||
except (json.JSONDecodeError, TypeError):
|
except (json.JSONDecodeError, TypeError):
|
||||||
pass
|
pass
|
||||||
@ -428,22 +373,18 @@ async def load_agent_config(pipeline_id: str = None, project_id: str = None) ->
|
|||||||
if raw:
|
if raw:
|
||||||
try:
|
try:
|
||||||
data = json.loads(raw) if isinstance(raw, str) else raw
|
data = json.loads(raw) if isinstance(raw, str) else raw
|
||||||
data["tools"] = _merge_tools(data.get("tools", []), SDLC_DEFAULT_TOOLS)
|
data["tools"] = _merge_tools(data.get("tools", []), tools)
|
||||||
# 产线缺省模型:agent_config 未显式设 model_name 时用 default_model 兜底
|
|
||||||
if not data.get("model_name") and default_model:
|
if not data.get("model_name") and default_model:
|
||||||
data["model_name"] = default_model
|
data["model_name"] = default_model
|
||||||
return AgentConfig.from_dict(data)
|
return AgentConfig.from_dict(data)
|
||||||
except (json.JSONDecodeError, TypeError):
|
except (json.JSONDecodeError, TypeError):
|
||||||
pass
|
pass
|
||||||
elif default_model:
|
elif default_model:
|
||||||
# agent_config 为空,仅产线设了缺省模型
|
|
||||||
cfg = AgentConfig.from_dict(SDLC_DEFAULT_CONFIG.to_dict())
|
|
||||||
cfg.model_name = default_model
|
cfg.model_name = default_model
|
||||||
return cfg
|
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
return SDLC_DEFAULT_CONFIG
|
return cfg
|
||||||
|
|
||||||
|
|
||||||
def _merge_tools(custom_tools: list, default_tools: list) -> list:
|
def _merge_tools(custom_tools: list, default_tools: list) -> list:
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user