feat: 项目-角色-模型配置 + 角色agent模型解析链
- sd_projects 加 default_model 字段(项目缺省模型=创建时的会话模型) - _t_create_project 记录 created_by + default_model - _resolve_llm_context 解析链:显式→项目-角色-模型(sd_project_role_models) →项目缺省模型(default_model)→RoleSpec→pipelines.default_model→deepseek-v4-pro - 新增工具 list_role_models / set_role_model(项目设置界面后端)
This commit is contained in:
parent
c50a5b9914
commit
14dca0cc43
@ -99,28 +99,47 @@ async def _resolve_role(project_id, role):
|
||||
async def _resolve_llm_context(sor, project_id, role, model_name=None):
|
||||
"""解析角色 agent 的 LLM 上下文:返回 (model_name, org_id)。
|
||||
|
||||
model_name 解析链(与会话 agent 的 load_agent_config 一致):
|
||||
model_name 解析链:
|
||||
1. 显式传入的 model_name
|
||||
2. RoleSpec.model_name(角色专属模型)
|
||||
3. 产线 default_model(pipelines.default_model)
|
||||
4. 全局默认 "deepseek-v4-pro"
|
||||
2. 项目-角色-模型(sd_project_role_models,设置界面配的)
|
||||
3. 用户当前模型(项目创建者 created_by → pipeline_agent_settings.default_llm_id → llm.name,缺省)
|
||||
4. RoleSpec.model_name(角色专属模型)
|
||||
5. 产线 default_model(pipelines.default_model)
|
||||
6. 全局默认 "deepseek-v4-pro"
|
||||
|
||||
org_id:从 sd_projects.org_id 读,传给 llm_bridge 做多租户隔离
|
||||
(llm 表查询只取「本机构 + 系统级 org_id='0'」的模型)。
|
||||
"""
|
||||
org_id = ""
|
||||
pipeline_id = ""
|
||||
default_model = ""
|
||||
try:
|
||||
recs = await sor.sqlExe(
|
||||
"SELECT org_id, pipeline_id FROM sd_projects WHERE id=${pid}$", {"pid": project_id})
|
||||
"SELECT org_id, pipeline_id, default_model FROM sd_projects WHERE id=${pid}$", {"pid": project_id})
|
||||
if recs:
|
||||
org_id = getattr(recs[0], "org_id", "") or ""
|
||||
pipeline_id = getattr(recs[0], "pipeline_id", "") or ""
|
||||
default_model = getattr(recs[0], "default_model", "") or ""
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# 2. 项目-角色-模型(设置界面配的,最高优先)
|
||||
if not model_name and project_id:
|
||||
try:
|
||||
recs = await sor.sqlExe(
|
||||
"SELECT model_name FROM sd_project_role_models WHERE project_id=${pid}$ AND role=${r}$",
|
||||
{"pid": project_id, "r": role})
|
||||
if recs and getattr(recs[0], "model_name", ""):
|
||||
model_name = getattr(recs[0], "model_name", "")
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# 3. 项目缺省模型(创建项目时记录的「当前会话模型」,session 级缺省)
|
||||
if not model_name and default_model:
|
||||
model_name = default_model
|
||||
|
||||
if not model_name:
|
||||
# 2. RoleSpec.model_name(角色专属模型)
|
||||
# 4. RoleSpec.model_name(角色专属模型)
|
||||
try:
|
||||
from pipeline_core import get_role_spec
|
||||
spec = get_role_spec(pipeline_id or await _resolve_pipeline_id(project_id), role)
|
||||
@ -129,7 +148,7 @@ async def _resolve_llm_context(sor, project_id, role, model_name=None):
|
||||
except Exception:
|
||||
pass
|
||||
if not model_name and pipeline_id:
|
||||
# 3. 产线 default_model
|
||||
# 5. 产线 default_model
|
||||
try:
|
||||
recs = await sor.sqlExe(
|
||||
"SELECT default_model FROM pipelines WHERE id=${pid}$", {"pid": pipeline_id})
|
||||
@ -138,7 +157,7 @@ async def _resolve_llm_context(sor, project_id, role, model_name=None):
|
||||
except Exception:
|
||||
pass
|
||||
if not model_name:
|
||||
# 4. 全局默认
|
||||
# 6. 全局默认
|
||||
model_name = "deepseek-v4-pro"
|
||||
return model_name, org_id
|
||||
|
||||
|
||||
@ -860,6 +860,8 @@ class AgentExecutor:
|
||||
"id": pid_val, "name": name, "description": desc,
|
||||
"status": "active", "org_id": org_id,
|
||||
"pipeline_id": self.space, "workspace_dir": workspace_dir,
|
||||
"created_by": self.user_id or "",
|
||||
"default_model": self.model_name or "",
|
||||
})
|
||||
await sor.C("sd_iterations", {
|
||||
"id": getID(), "project_id": pid_val,
|
||||
|
||||
@ -178,6 +178,8 @@ SDL_TOOLS = [
|
||||
),
|
||||
# ── 项目生命周期 ──
|
||||
ToolDefinition(name="list_projects", description="列出项目列表(含状态)", parameters={"status": "按状态筛选(可选)"}, category="project"),
|
||||
ToolDefinition(name="list_role_models", description="列出项目各角色的模型配置(缺省用项目缺省模型)", parameters={"project_id": "项目ID"}, category="project"),
|
||||
ToolDefinition(name="set_role_model", description="设置某角色的模型(如 deploy_test 用 qwen3.8-max;model_name 空=清除回退缺省)", parameters={"project_id": "项目ID", "role": "角色名(agent.develop/agent.deploy_test等)", "model_name": "模型名(llm.name,空=回退缺省)"}, category="project"),
|
||||
ToolDefinition(name="start_project", description="启动项目(draft/archived→active)", parameters={"project_id": "项目ID"}, category="project"),
|
||||
ToolDefinition(name="complete_project", description="完成项目(active→completed)", parameters={"project_id": "项目ID"}, category="project"),
|
||||
ToolDefinition(name="archive_project", description="归档项目(completed→archived)", parameters={"project_id": "项目ID"}, category="project"),
|
||||
@ -1168,6 +1170,51 @@ async def _h_start_project(sor, p, ctx):
|
||||
return f"OK: {msg}" if ok else f"ERROR: {msg}"
|
||||
|
||||
|
||||
async def _h_list_role_models(sor, p, ctx):
|
||||
"""列出项目各角色的模型配置(sd_project_role_models)。"""
|
||||
pid = (p.get("project_id", "") or "").strip() or (ctx.get("project_id", "") or "")
|
||||
if not pid:
|
||||
return "需要 project_id"
|
||||
pid = await _resolve_id(sor, "sd_projects", pid) or pid
|
||||
recs = await sor.sqlExe(
|
||||
"SELECT role, model_name FROM sd_project_role_models WHERE project_id=${pid}$ ORDER BY role",
|
||||
{"pid": pid})
|
||||
await sor.sqlExe("COMMIT", {})
|
||||
if not recs:
|
||||
return "(无角色模型配置,所有角色用项目缺省模型)"
|
||||
lines = []
|
||||
for r in recs:
|
||||
lines.append(f"- {r.role} -> {r.model_name or '(空=缺省)'}")
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
async def _h_set_role_model(sor, p, ctx):
|
||||
"""设置某角色的模型(model_name 空 = 清除,回退项目缺省模型)。"""
|
||||
pid = (p.get("project_id", "") or "").strip() or (ctx.get("project_id", "") or "")
|
||||
role = (p.get("role", "") or "").strip()
|
||||
model_name = (p.get("model_name", "") or "").strip()
|
||||
if not pid:
|
||||
return "需要 project_id"
|
||||
if not role:
|
||||
return "需要 role"
|
||||
pid = await _resolve_id(sor, "sd_projects", pid) or pid
|
||||
from appPublic.uniqueID import getID
|
||||
recs = await sor.sqlExe(
|
||||
"SELECT id FROM sd_project_role_models WHERE project_id=${pid}$ AND role=${r}$",
|
||||
{"pid": pid, "r": role})
|
||||
await sor.sqlExe("COMMIT", {})
|
||||
if recs:
|
||||
await sor.sqlExe(
|
||||
"UPDATE sd_project_role_models SET model_name=${m}$, updated_at=NOW() WHERE id=${id}$",
|
||||
{"m": model_name or None, "id": getattr(recs[0], "id", "")})
|
||||
else:
|
||||
await sor.C("sd_project_role_models", {
|
||||
"id": getID(), "project_id": pid, "role": role, "model_name": model_name or None,
|
||||
})
|
||||
await sor.sqlExe("COMMIT", {})
|
||||
return f"OK: {role} 模型 -> {model_name or '(空=用项目缺省模型)'}"
|
||||
|
||||
|
||||
async def _h_complete_project(sor, p, ctx):
|
||||
pid = (p.get("project_id", "") or "").strip()
|
||||
if not pid:
|
||||
@ -1760,6 +1807,8 @@ SDL_HANDLERS = {
|
||||
"verify_feature": _h_verify_feature,
|
||||
# ── 项目生命周期 ──
|
||||
"list_projects": _h_list_projects,
|
||||
"list_role_models": _h_list_role_models,
|
||||
"set_role_model": _h_set_role_model,
|
||||
"start_project": _h_start_project,
|
||||
"complete_project": _h_complete_project,
|
||||
"archive_project": _h_archive_project,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user