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,
|
||||
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
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(
|
||||
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 创建任务(可按 requirement→design→develop 拆分),再 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_config(sd_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:
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user