fix: delete agent 500 error + dynamic personality + deployment guide
- Fix delete agent 500: clean up FK records (agent_llm_logs, permissions, schedules, executions, team_members) and unbind goals/tasks before delete - Remove hardcoded personality templates in Android, replace with dynamic system prompt generation from name + description - Set promptSectionsEnabled=false to bypass PromptComposer for personality - Add Tencent Cloud Linux deployment guide (Docker Compose) - Accumulated backend service updates, frontend UI fixes, Android app changes Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,8 +1,9 @@
|
||||
"""
|
||||
Agent管理API
|
||||
"""
|
||||
from fastapi import APIRouter, Depends, HTTPException, status, Query, Response
|
||||
from fastapi import APIRouter, Depends, HTTPException, status, Query, Response, Request
|
||||
from sqlalchemy.orm import Session
|
||||
from sqlalchemy import text
|
||||
from pydantic import BaseModel, Field
|
||||
from typing import List, Optional, Dict, Any, Union
|
||||
from datetime import datetime
|
||||
@@ -61,16 +62,18 @@ class SceneTemplateItem(BaseModel):
|
||||
category: Optional[str] = None
|
||||
default_temperature: Optional[float] = None
|
||||
parameter_hints: List[str] = Field(default_factory=list)
|
||||
contract_id: Optional[str] = None
|
||||
|
||||
|
||||
class AgentFromSceneTemplateCreate(BaseModel):
|
||||
"""从场景模板创建 Agent"""
|
||||
"""从场景模板创建 Agent(支持 DSL 契约参数)"""
|
||||
|
||||
template_id: str
|
||||
name: str
|
||||
description: Optional[str] = None
|
||||
parameters: Dict[str, Any] = Field(default_factory=dict)
|
||||
budget_config: Optional[Dict[str, Any]] = None
|
||||
contract_id: Optional[str] = None
|
||||
|
||||
|
||||
class PreviewChatTurnResponse(BaseModel):
|
||||
@@ -96,6 +99,9 @@ class AgentResponse(BaseModel):
|
||||
version: int
|
||||
status: str
|
||||
user_id: Optional[str] # 允许为None
|
||||
parent_agent_id: Optional[str] = None
|
||||
agent_type: Optional[str] = None
|
||||
category: Optional[str] = None
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
|
||||
@@ -110,13 +116,14 @@ async def get_agents(
|
||||
limit: int = Query(100, ge=1, le=100, description="每页记录数"),
|
||||
search: Optional[str] = Query(None, description="搜索关键词(按名称或描述)"),
|
||||
status: Optional[str] = Query(None, description="状态筛选"),
|
||||
workspace_id: Optional[str] = Query(None, description="工作区ID筛选"),
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user)
|
||||
):
|
||||
"""
|
||||
获取Agent列表
|
||||
|
||||
支持分页、搜索、状态筛选
|
||||
|
||||
支持分页、搜索、状态筛选、工作区筛选
|
||||
"""
|
||||
# 管理员可以看到所有Agent,普通用户只能看到自己拥有的或有read权限的
|
||||
if current_user.role == "admin":
|
||||
@@ -125,10 +132,10 @@ async def get_agents(
|
||||
# 获取用户拥有或有read权限的Agent
|
||||
from sqlalchemy import or_
|
||||
from app.models.permission import AgentPermission
|
||||
|
||||
|
||||
# 用户拥有的Agent
|
||||
owned_agents = db.query(Agent.id).filter(Agent.user_id == current_user.id).subquery()
|
||||
|
||||
|
||||
# 用户有read权限的Agent(通过用户ID或角色)
|
||||
user_permissions = db.query(AgentPermission.agent_id).filter(
|
||||
AgentPermission.permission_type == "read",
|
||||
@@ -137,14 +144,18 @@ async def get_agents(
|
||||
AgentPermission.role_id.in_([r.id for r in current_user.roles])
|
||||
)
|
||||
).subquery()
|
||||
|
||||
|
||||
query = db.query(Agent).filter(
|
||||
or_(
|
||||
Agent.id.in_(db.query(owned_agents.c.id)),
|
||||
Agent.id.in_(db.query(user_permissions.c.agent_id))
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
# 工作区筛选
|
||||
if workspace_id:
|
||||
query = query.filter(Agent.workspace_id == workspace_id)
|
||||
|
||||
# 搜索:按名称或描述搜索
|
||||
if search:
|
||||
search_pattern = f"%{search}%"
|
||||
@@ -187,26 +198,36 @@ async def get_agents(
|
||||
@router.post("", response_model=AgentResponse, status_code=status.HTTP_201_CREATED)
|
||||
async def create_agent(
|
||||
agent_data: AgentCreate,
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user)
|
||||
):
|
||||
"""
|
||||
创建Agent
|
||||
|
||||
|
||||
创建时会验证工作流配置的有效性
|
||||
"""
|
||||
# 从 JWT 提取当前工作区 ID
|
||||
from app.core.security import decode_access_token
|
||||
ws_id = None
|
||||
auth_header = request.headers.get("Authorization", "")
|
||||
if auth_header.startswith("Bearer "):
|
||||
payload = decode_access_token(auth_header[7:])
|
||||
if payload:
|
||||
ws_id = payload.get("ws") or None
|
||||
|
||||
# 验证工作流配置
|
||||
if "nodes" not in agent_data.workflow_config or "edges" not in agent_data.workflow_config:
|
||||
raise ValidationError("工作流配置必须包含nodes和edges")
|
||||
|
||||
|
||||
nodes = agent_data.workflow_config.get("nodes", [])
|
||||
edges = agent_data.workflow_config.get("edges", [])
|
||||
|
||||
|
||||
# 验证工作流
|
||||
validation_result = validate_workflow(nodes, edges)
|
||||
if not validation_result["valid"]:
|
||||
raise ValidationError(f"工作流配置验证失败: {', '.join(validation_result['errors'])}")
|
||||
|
||||
|
||||
# 检查名称是否重复
|
||||
existing_agent = db.query(Agent).filter(
|
||||
Agent.name == agent_data.name,
|
||||
@@ -214,7 +235,7 @@ async def create_agent(
|
||||
).first()
|
||||
if existing_agent:
|
||||
raise ConflictError(f"Agent名称 '{agent_data.name}' 已存在")
|
||||
|
||||
|
||||
# 创建Agent
|
||||
agent = Agent(
|
||||
name=agent_data.name,
|
||||
@@ -222,6 +243,7 @@ async def create_agent(
|
||||
workflow_config=agent_data.workflow_config,
|
||||
budget_config=agent_data.budget_config,
|
||||
user_id=current_user.id,
|
||||
workspace_id=ws_id,
|
||||
status="draft",
|
||||
category=agent_data.category,
|
||||
tags=agent_data.tags,
|
||||
@@ -377,9 +399,33 @@ async def delete_agent(
|
||||
raise HTTPException(status_code=403, detail="无权删除此Agent")
|
||||
|
||||
agent_name = agent.name
|
||||
|
||||
# 1. 清理直接关联的记录(级联删除)
|
||||
related_tables = [
|
||||
"agent_llm_logs",
|
||||
"agent_permissions",
|
||||
"agent_schedules",
|
||||
"executions",
|
||||
"team_members",
|
||||
]
|
||||
for table in related_tables:
|
||||
db.execute(text(f"DELETE FROM {table} WHERE agent_id = :aid"), {"aid": agent_id})
|
||||
logger.debug(f"已清理 {table} 中 agent_id={agent_id} 的记录")
|
||||
|
||||
# 2. 解除分配关系(设为 NULL)
|
||||
db.execute(
|
||||
text("UPDATE goals SET main_agent_id = NULL WHERE main_agent_id = :aid"),
|
||||
{"aid": agent_id},
|
||||
)
|
||||
db.execute(
|
||||
text("UPDATE tasks SET assigned_agent_id = NULL WHERE assigned_agent_id = :aid"),
|
||||
{"aid": agent_id},
|
||||
)
|
||||
|
||||
# 3. 删除 Agent 自身
|
||||
db.delete(agent)
|
||||
db.commit()
|
||||
|
||||
|
||||
logger.info(f"用户 {current_user.username} 删除了Agent: {agent_name} ({agent_id})")
|
||||
return {"message": "Agent已删除"}
|
||||
|
||||
@@ -773,3 +819,67 @@ async def import_agent(
|
||||
db.commit()
|
||||
db.refresh(agent)
|
||||
return agent
|
||||
|
||||
|
||||
# ——— Agent 间知识共享 API ———
|
||||
|
||||
class InheritKnowledgeResponse(BaseModel):
|
||||
child_agent_id: str
|
||||
parent_agent_id: str
|
||||
knowledge_entries_copied: int
|
||||
global_knowledge_shared: int
|
||||
learning_patterns_copied: int
|
||||
message: str
|
||||
|
||||
|
||||
@router.post("/{agent_id}/inherit-knowledge", response_model=InheritKnowledgeResponse)
|
||||
def inherit_agent_knowledge(
|
||||
agent_id: str,
|
||||
parent_agent_id: str = Query(..., description="父 Agent ID"),
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user),
|
||||
):
|
||||
"""让子 Agent 从父 Agent 继承知识条目、全局知识和学习模式。"""
|
||||
child = db.query(Agent).filter(Agent.id == agent_id).first()
|
||||
if not child:
|
||||
raise NotFoundError("Agent", agent_id)
|
||||
parent = db.query(Agent).filter(Agent.id == parent_agent_id).first()
|
||||
if not parent:
|
||||
raise NotFoundError("父 Agent", parent_agent_id)
|
||||
|
||||
# 记录父子关系
|
||||
child.parent_agent_id = parent_agent_id
|
||||
db.commit()
|
||||
|
||||
try:
|
||||
from app.services.knowledge_sharing import inherit_knowledge_from_parent
|
||||
result = inherit_knowledge_from_parent(agent_id, parent_agent_id, db)
|
||||
result["message"] = f"已从父 Agent 继承 {result['knowledge_entries_copied']} 条知识、" \
|
||||
f"{result['global_knowledge_shared']} 条全局知识、" \
|
||||
f"{result['learning_patterns_copied']} 条学习模式"
|
||||
return InheritKnowledgeResponse(**result)
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=f"知识继承失败: {e}")
|
||||
|
||||
|
||||
@router.get("/{agent_id}/parent-knowledge")
|
||||
def get_parent_knowledge_preview(
|
||||
agent_id: str,
|
||||
limit: int = Query(5, ge=1, le=20),
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user),
|
||||
):
|
||||
"""预览 Agent 可用的父级知识(不实际继承)。"""
|
||||
agent = db.query(Agent).filter(Agent.id == agent_id).first()
|
||||
if not agent:
|
||||
raise NotFoundError("Agent", agent_id)
|
||||
|
||||
from app.services.knowledge_sharing import get_parent_knowledge_context
|
||||
context = get_parent_knowledge_context(agent_id, max_entries=limit, db=db)
|
||||
|
||||
return {
|
||||
"agent_id": agent_id,
|
||||
"parent_agent_id": getattr(agent, "parent_agent_id", None),
|
||||
"has_parent_knowledge": bool(context),
|
||||
"context_preview": context[:2000] if context else "",
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user