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:
2026-06-29 01:17:21 +08:00
parent 86b98865e3
commit beff3fac8d
1084 changed files with 117315 additions and 1281 deletions

View File

@@ -8,7 +8,7 @@ from __future__ import annotations
import logging
from typing import Any, Dict, List, Optional
from fastapi import APIRouter, Depends, HTTPException, Query, Path
from fastapi import APIRouter, Depends, HTTPException, Query, Path, Request
from pydantic import BaseModel
from sqlalchemy.orm import Session
@@ -33,6 +33,8 @@ class ToolCreate(BaseModel):
implementation_type: str # builtin / http / code / workflow
implementation_config: Optional[dict] = None
is_public: bool = False
status: str = "active" # active / draft / deprecated
tags: Optional[List[str]] = None
class ToolResponse(BaseModel):
@@ -44,6 +46,8 @@ class ToolResponse(BaseModel):
implementation_type: str
implementation_config: Optional[dict] = None
is_public: bool = False
status: str = "active"
tags: Optional[List[str]] = None
use_count: int = 0
user_id: Optional[str] = None
created_at: str = ""
@@ -86,6 +90,8 @@ def _tool_to_dict(tool: Tool) -> dict:
"implementation_type": tool.implementation_type,
"implementation_config": tool.implementation_config,
"is_public": tool.is_public,
"status": tool.status if hasattr(tool, "status") else "active",
"tags": tool.tags if hasattr(tool, "tags") else None,
"use_count": tool.use_count,
"user_id": tool.user_id,
"created_at": tool.created_at.isoformat() if tool.created_at else "",
@@ -139,12 +145,18 @@ async def list_tools(
category: Optional[str] = Query(None, description="按分类筛选"),
search: Optional[str] = Query(None, description="搜索关键词"),
scope: Optional[str] = Query("public", description="public / mine / all"),
status: Optional[str] = Query(None, description="状态筛选: active/draft/deprecated"),
workspace_id: Optional[str] = Query(None, description="工作区ID筛选"),
db: Session = Depends(get_db),
current_user: Optional[User] = Depends(get_current_user),
):
"""浏览工具市场(含内置工具 + 数据库工具)。"""
query = db.query(Tool)
# 工作区筛选
if workspace_id:
query = query.filter(Tool.workspace_id == workspace_id)
if scope == "public":
query = query.filter(Tool.is_public == True)
elif scope == "mine":
@@ -152,6 +164,12 @@ async def list_tools(
raise HTTPException(status_code=401, detail="需登录")
query = query.filter(Tool.user_id == current_user.id)
if status:
query = query.filter(Tool.status == status)
else:
# 默认只显示 active 状态的工具(排除 deprecated
query = query.filter((Tool.status == "active") | (Tool.status == None))
if category:
query = query.filter(Tool.category == category)
if search:
@@ -216,10 +234,20 @@ async def get_tool(
@router.post("", response_model=ToolResponse, status_code=201)
async def create_tool(
tool_data: ToolCreate,
request: Request,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user),
):
"""创建自定义工具。"""
# 从 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
existing = db.query(Tool).filter(Tool.name == tool_data.name).first()
if existing:
raise HTTPException(status_code=400, detail=f"工具名 '{tool_data.name}' 已存在")
@@ -237,20 +265,24 @@ async def create_tool(
implementation_type=tool_data.implementation_type,
implementation_config=tool_data.implementation_config,
is_public=tool_data.is_public,
status=tool_data.status,
tags=tool_data.tags,
user_id=current_user.id,
workspace_id=ws_id,
)
db.add(tool)
db.commit()
db.refresh(tool)
logger.info("工具已创建: %s (type=%s)", tool.name, tool.implementation_type)
logger.info("工具已创建: %s (type=%s, status=%s)", tool.name, tool.implementation_type, tool.status)
# 刷新注册表
tool_registry._custom_tool_configs[tool.name] = {
**(tool.implementation_config or {}),
"_type": tool.implementation_type,
"_db_id": tool.id,
}
tool_registry._tool_schemas[tool.name] = tool.function_schema
# 仅 active 状态注入注册表
if tool.status == "active" or tool.status is None:
tool_registry._custom_tool_configs[tool.name] = {
**(tool.implementation_config or {}),
"_type": tool.implementation_type,
"_db_id": tool.id,
}
tool_registry._tool_schemas[tool.name] = tool.function_schema
return _tool_to_dict(tool)
@@ -317,12 +349,27 @@ async def delete_tool(
db.commit()
# 清理注册表
tool_registry._custom_tool_configs.pop(tool.name, None)
tool_registry._tool_schemas.pop(tool.name, None)
tool_registry.unregister_tool(tool.name)
return {"message": "工具已删除"}
@router.post("/reload")
async def reload_tools(db: Session = Depends(get_db)):
"""从数据库重新加载所有自定义工具到注册表(热更新)。"""
# 清除旧的自定义工具
for name in list(tool_registry._custom_tool_configs.keys()):
if name not in tool_registry._builtin_tools:
tool_registry._custom_tool_configs.pop(name, None)
tool_registry._tool_schemas.pop(name, None)
# 重新加载
tool_registry.load_tools_from_db(db)
count = len(tool_registry._custom_tool_configs)
logger.info("工具热更新完成,已加载 %d 个自定义工具", count)
return {"message": f"工具注册表已刷新", "custom_tool_count": count}
# ─── 工具测试 ──────────────────────────────────────────────────