refactor: core剥离SDLC能力——GENERAL_TOOLS通用内核+PipelineAbility可插拔能力包注册表+load_agent_config按pipeline_id动态挂载

This commit is contained in:
yumoqing 2026-08-15 09:26:01 +08:00
parent 63760ff186
commit 9635825164
3 changed files with 143 additions and 118 deletions

View File

@ -20,11 +20,22 @@ from .agent_config import (
MemoryConfig,
SkillConfig,
ToolDefinition,
GENERAL_TOOLS,
DEFAULT_AGENT_CONFIG,
DEFAULT_ABILITY_ID,
SDLC_DEFAULT_CONFIG,
SDLC_DEFAULT_TOOLS,
load_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 (
ToolRegistry,
get_tool_registry,

73
pipeline_core/ability.py Normal file
View 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)

View File

@ -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(
name="switch_project",
description="切换到指定项目。用户说「切换到XXX」时调用。",
@ -191,82 +196,6 @@ SDLC_DEFAULT_TOOLS = [
parameters={"name": "项目名称", "description": "项目描述"},
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(
name="run_command",
description="在工作空间中执行shell命令",
@ -274,18 +203,6 @@ SDLC_DEFAULT_TOOLS = [
category="shell",
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 新增 ──
ToolDefinition(
name="ask_user",
@ -339,36 +256,26 @@ SDLC_DEFAULT_TOOLS = [
]
SDLC_DEFAULT_CONFIG = AgentConfig(
DEFAULT_AGENT_CONFIG = AgentConfig(
model_name="deepseek-v4-pro",
temperature=0.4,
max_turns=30,
system_prompt="""你是 SDLC 开发产线的驾驶舱 agent。你的职责在自动化项目实施过程中主动发现问题、定位根因、用工具解决问题推动项目前进。
## 你的身份
你是一个具备通用推理与判断能力的 agent在此之上额外配备了项目实施工具像一名有经验的工程负责人那样思考先理解意图再拆解问题判断自己能否解决必要时澄清或诚实说明
system_prompt="""你是一个通用 agent具备通用推理与判断能力并根据当前产线配备了对应的工具。像一名有经验的负责人那样思考先理解意图再拆解问题判断自己能否解决必要时澄清或诚实说明。
## 工作原则
1. 先理解用户意图再决定行动意图模糊时用 ask_user 澄清不要臆测
2. 用工具是手段不是目的工具能解决就用工具用工具只是为了把事做成
3. 能力自省与诚实降级如果用户的请求超出你的工具能力例如"重做已完成任务"但你没有重置任务状态的工具不要硬套一个不相关的工具你必须
3. 能力自省与诚实降级如果用户的请求超出你的工具能力不要硬套一个不相关的工具你必须
a) 明确告诉用户你做不到缺什么能力为什么
b) 给出你能做到的替代方案如用 create_task 新建重做任务
b) 给出你能做到的替代方案
c) 必要时 ask_user 让用户拍板
4. 发现项目卡点任务卡死审核超时失败僵尸 claimed_by等待无响应主动定位根因并推动修复而不是只列一张诊断清单
4. 发现异常/卡点时主动定位根因并推动解决而不是只列清单
5. 每轮输出 tool_call / reply / ask_user 三者之一根据实际情况选择没有任何强制
## 典型场景(帮助判断何时用哪个工具,非强制)
- 用户提出新的开发需求/功能"实现XX""XX系统要做XX" 通常用 create_task 创建任务可按 requirementdesigndevelop 拆分 start_agents 启动
- 用户问进展/状态 list_tasks / diagnose_project / check_progress
- 用户问待回答问题 list_questions / answer_question
- 用户报告异常/故障 先用 run_command / diagnose_project 定位根因再决定修复动作
- 用户要求做你工具能力之外的事 "能力自省"原则诚实说明 + 给替代方案 + 必要时 ask_user
## 工具集
{tools_description}
""",
tools=SDLC_DEFAULT_TOOLS,
tools=GENERAL_TOOLS,
session_isolation="project",
history_limit=50,
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:
"""加载产线的 Agent 配置。
"""加载产线的 Agent 配置(通用内核 + 可插拔产线能力)
优先级
1. 项目级 sd_org_settings.agent_configsd_org_settings org_id 为键由项目解析 org_id
2. 产线级 pipelines.agent_config
3. SDLC_DEFAULT_CONFIG
组装顺序
1. 基础 = DEFAULT_AGENT_CONFIG通用心智 + GENERAL_TOOLS
2. 产线能力 = get_ability(pipeline_id) 动态挂载工具 + prompt 片段
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:
from sqlor.dbpools import DBPools
@ -411,7 +356,7 @@ async def load_agent_config(pipeline_id: str = None, project_id: str = None) ->
if raw:
try:
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)
except (json.JSONDecodeError, TypeError):
pass
@ -428,22 +373,18 @@ async def load_agent_config(pipeline_id: str = None, project_id: str = None) ->
if raw:
try:
data = json.loads(raw) if isinstance(raw, str) else raw
data["tools"] = _merge_tools(data.get("tools", []), SDLC_DEFAULT_TOOLS)
# 产线缺省模型agent_config 未显式设 model_name 时用 default_model 兜底
data["tools"] = _merge_tools(data.get("tools", []), tools)
if not data.get("model_name") and default_model:
data["model_name"] = default_model
return AgentConfig.from_dict(data)
except (json.JSONDecodeError, TypeError):
pass
elif default_model:
# agent_config 为空,仅产线设了缺省模型
cfg = AgentConfig.from_dict(SDLC_DEFAULT_CONFIG.to_dict())
cfg.model_name = default_model
return cfg
except Exception:
pass
return SDLC_DEFAULT_CONFIG
return cfg
def _merge_tools(custom_tools: list, default_tools: list) -> list: