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

@@ -29,5 +29,9 @@ from app.models.knowledge_entry import KnowledgeEntry
from app.models.user_fingerprint import UserFingerprint
from app.models.shadow_comparison import ShadowComparison
from app.models.feedback_record import FeedbackRecord
from app.models.audit_log import AuditLog
from app.models.workspace import Workspace, WorkspaceMembership
from app.models.scene_contract import SceneContract
from app.models.team import Team, TeamMember
__all__ = ["User", "Workflow", "WorkflowVersion", "Agent", "GlobalKnowledge", "AgentRating", "AgentFavorite", "Execution", "ExecutionLog", "ModelConfig", "DataSource", "WorkflowTemplate", "TemplateRating", "TemplateFavorite", "NodeTemplate", "Role", "Permission", "WorkflowPermission", "AgentPermission", "AlertRule", "AlertLog", "PersistentUserMemory", "AgentLLMLog", "AgentVectorMemory", "AgentLearningPattern", "AgentSchedule", "KnowledgeBase", "Document", "DocumentChunk", "Notification", "UserFeishuOpenId", "NodePlugin", "OrchestrationTemplate", "Goal", "Task", "AgentExecutionLog", "UserBehaviorLog", "KnowledgeEntry", "UserFingerprint", "ShadowComparison", "FeedbackRecord"]
__all__ = ["User", "Workflow", "WorkflowVersion", "Agent", "GlobalKnowledge", "AgentRating", "AgentFavorite", "Execution", "ExecutionLog", "ModelConfig", "DataSource", "WorkflowTemplate", "TemplateRating", "TemplateFavorite", "NodeTemplate", "Role", "Permission", "WorkflowPermission", "AgentPermission", "AlertRule", "AlertLog", "PersistentUserMemory", "AgentLLMLog", "AgentVectorMemory", "AgentLearningPattern", "AgentSchedule", "KnowledgeBase", "Document", "DocumentChunk", "Notification", "UserFeishuOpenId", "NodePlugin", "OrchestrationTemplate", "Goal", "Task", "AgentExecutionLog", "UserBehaviorLog", "KnowledgeEntry", "UserFingerprint", "ShadowComparison", "FeedbackRecord", "AuditLog", "Workspace", "WorkspaceMembership", "SceneContract", "Team", "TeamMember"]

View File

@@ -1,7 +1,7 @@
"""
智能体模型
"""
from sqlalchemy import Column, String, Text, Integer, DateTime, JSON, ForeignKey, func
from sqlalchemy import Column, String, Text, Integer, DateTime, JSON, ForeignKey, Index, func
from sqlalchemy.dialects.mysql import CHAR
from sqlalchemy.orm import relationship
from app.core.database import Base
@@ -27,6 +27,7 @@ class Agent(Base):
version = Column(Integer, default=1, comment="版本号")
status = Column(String(20), default="draft", comment="状态: draft/published/running/stopped")
user_id = Column(CHAR(36), ForeignKey("users.id"), comment="创建者ID")
workspace_id = Column(CHAR(36), ForeignKey("workspaces.id"), nullable=True, comment="所属工作区ID")
# 技能市场字段
category = Column(String(50), nullable=True, comment="分类: llm/data_processing/automation/integration/other")
@@ -39,6 +40,7 @@ class Agent(Base):
use_count = Column(Integer, default=0, comment="被安装次数")
view_count = Column(Integer, default=0, comment="查看次数")
forked_from_id = Column(CHAR(36), nullable=True, comment="从哪个Agent Fork而来市场安装")
parent_agent_id = Column(CHAR(36), nullable=True, comment="父 Agent ID自主创建时继承经验")
created_at = Column(DateTime, default=func.now(), comment="创建时间")
updated_at = Column(DateTime, default=func.now(), onupdate=func.now(), comment="更新时间")

View File

@@ -1,7 +1,7 @@
"""
Agent LLM 调用日志模型 — 记录每次 Agent Runtime 发起的 LLM 调用
"""
from sqlalchemy import Column, String, Text, Integer, DateTime, ForeignKey, func
from sqlalchemy import Column, String, Text, Integer, DateTime, ForeignKey, Index, func
from sqlalchemy.dialects.mysql import CHAR
from app.core.database import Base
import uuid
@@ -27,3 +27,11 @@ class AgentLLMLog(Base):
status = Column(String(20), default="success", comment="状态: success/error")
error_message = Column(Text, nullable=True, comment="错误信息")
created_at = Column(DateTime, default=func.now(), comment="创建时间")
__table_args__ = (
Index("idx_llm_agent_id", "agent_id"),
Index("idx_llm_user_id", "user_id"),
Index("idx_llm_session_id", "session_id"),
Index("idx_llm_created_at", "created_at"),
Index("idx_llm_agent_created", "agent_id", "created_at"),
)

View File

@@ -1,7 +1,7 @@
"""Agent 定时任务表:按 cron 表达式周期执行 Agent"""
import uuid
from datetime import datetime
from sqlalchemy import Column, String, Text, Integer, DateTime, ForeignKey, Boolean, JSON
from sqlalchemy import Column, String, Text, Integer, DateTime, ForeignKey, Boolean, JSON, Index
from sqlalchemy.dialects.mysql import CHAR
from app.core.database import Base
@@ -25,6 +25,7 @@ class AgentSchedule(Base):
last_run_status = Column(String(32), nullable=True, comment="上次执行状态: success/failed")
next_run_at = Column(DateTime, nullable=False, comment="下次执行时间")
user_id = Column(CHAR(36), ForeignKey("users.id"), nullable=False, index=True, comment="创建者 ID")
workspace_id = Column(CHAR(36), ForeignKey("workspaces.id"), nullable=True, comment="所属工作区ID")
created_at = Column(DateTime, default=datetime.utcnow, comment="创建时间")
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, comment="更新时间")

View File

@@ -1,7 +1,7 @@
"""
告警规则模型
"""
from sqlalchemy import Column, String, Text, Integer, Boolean, DateTime, ForeignKey, JSON, func
from sqlalchemy import Column, String, Text, Integer, Boolean, DateTime, ForeignKey, Index, JSON, func
from sqlalchemy.dialects.mysql import CHAR
from sqlalchemy.orm import relationship
from app.core.database import Base
@@ -45,6 +45,12 @@ class AlertRule(Base):
created_at = Column(DateTime, default=func.now(), comment="创建时间")
updated_at = Column(DateTime, default=func.now(), onupdate=func.now(), comment="更新时间")
__table_args__ = (
Index("idx_ar_alert_type", "alert_type"),
Index("idx_ar_enabled", "enabled"),
Index("idx_ar_target_type", "target_type"),
)
# 关系
user = relationship("User", backref="alert_rules")
@@ -77,6 +83,12 @@ class AlertLog(Base):
acknowledged_at = Column(DateTime, comment="确认时间")
acknowledged_by = Column(CHAR(36), ForeignKey("users.id"), nullable=True, comment="确认人ID")
__table_args__ = (
Index("idx_al_rule_id", "rule_id"),
Index("idx_al_status", "status"),
Index("idx_al_triggered_at", "triggered_at"),
)
# 关系
rule = relationship("AlertRule", backref="logs")
acknowledged_user = relationship("User", foreign_keys=[acknowledged_by])

View File

@@ -0,0 +1,26 @@
"""
操作审计日志模型
"""
import uuid
from sqlalchemy import Column, String, Text, DateTime, JSON, func
from sqlalchemy.dialects.mysql import CHAR
from app.core.database import Base
class AuditLog(Base):
__tablename__ = "audit_logs"
id = Column(CHAR(36), primary_key=True, default=lambda: str(uuid.uuid4()), comment="日志ID")
user_id = Column(CHAR(36), nullable=False, index=True, comment="操作用户ID")
username = Column(String(100), nullable=False, comment="操作用户名")
action = Column(String(50), nullable=False, index=True, comment="操作类型: CREATE/UPDATE/DELETE/EXECUTE/LOGIN")
resource_type = Column(String(100), nullable=False, comment="资源类型: agent/workflow/user/permission/...")
resource_id = Column(String(100), nullable=True, comment="资源ID")
resource_name = Column(String(255), nullable=True, comment="资源名称")
detail = Column(JSON, nullable=True, comment="操作详情")
ip_address = Column(String(45), nullable=True, comment="客户端IP")
status = Column(String(20), nullable=False, default="success", comment="操作状态: success/failure")
created_at = Column(DateTime, default=func.now(), index=True, comment="操作时间")
def __repr__(self):
return f"<AuditLog(id={self.id}, user={self.username}, action={self.action}, resource={self.resource_type})>"

View File

@@ -0,0 +1,43 @@
"""
对话分支模型 — 支持从任意历史点分叉对话
参考 Claude Code src/commands/branch/branch.ts
"""
from __future__ import annotations
import uuid
from datetime import datetime
from sqlalchemy import Column, String, Text, Integer, DateTime, JSON, Boolean
from sqlalchemy.dialects.mysql import CHAR
from app.core.database import Base
class ConversationBranch(Base):
"""对话分支 — 保存某个时间点的完整对话快照"""
__tablename__ = "conversation_branches"
id = Column(CHAR(36), primary_key=True, default=lambda: str(uuid.uuid4()), comment="分支ID")
user_id = Column(String(36), nullable=False, index=True, comment="用户ID")
agent_id = Column(String(36), nullable=True, comment="关联Agent ID")
agent_name = Column(String(200), nullable=True, comment="Agent 名称(冗余便于展示)")
# 溯源链
parent_session_id = Column(String(100), nullable=False, comment="分叉来源会话ID")
branch_session_id = Column(String(100), nullable=False, unique=True, comment="新分支会话ID")
# 元信息
title = Column(String(500), nullable=False, comment="分支标题(取自首条用户消息)")
message_count = Column(Integer, default=0, comment="快照包含的消息数")
first_user_message = Column(Text, nullable=True, comment="首条用户消息(用于列表展示)")
# 完整消息快照 (JSON)
messages = Column(JSON, nullable=True, comment="分支创建时的完整消息列表")
# 状态
is_active = Column(Boolean, default=True, comment="是否活跃")
created_at = Column(DateTime, default=datetime.now, comment="创建时间")
def __repr__(self):
return f"<ConversationBranch(id={self.id}, title={self.title}, messages={self.message_count})>"

View File

@@ -1,7 +1,7 @@
"""
数据源模型
"""
from sqlalchemy import Column, String, Text, DateTime, JSON, ForeignKey, func
from sqlalchemy import Column, String, Text, DateTime, JSON, ForeignKey, Index, func
from sqlalchemy.dialects.mysql import CHAR
from sqlalchemy.orm import relationship
from app.core.database import Base
@@ -19,6 +19,7 @@ class DataSource(Base):
config = Column(JSON, nullable=False, comment="连接配置(加密存储敏感信息)")
status = Column(String(20), default="active", comment="状态: active/inactive/error")
user_id = Column(CHAR(36), ForeignKey("users.id"), nullable=False, comment="创建者ID")
workspace_id = Column(CHAR(36), ForeignKey("workspaces.id"), nullable=True, comment="所属工作区ID")
last_connected_at = Column(DateTime, comment="最后连接时间")
created_at = Column(DateTime, default=func.now(), comment="创建时间")
updated_at = Column(DateTime, default=func.now(), onupdate=func.now(), comment="更新时间")
@@ -26,5 +27,9 @@ class DataSource(Base):
# 关系
user = relationship("User", backref="data_sources")
__table_args__ = (
Index("ix_data_sources_workspace", "workspace_id"),
)
def __repr__(self):
return f"<DataSource(id={self.id}, name={self.name}, type={self.type})>"

View File

@@ -1,7 +1,7 @@
"""
执行记录模型
"""
from sqlalchemy import Column, String, Text, Integer, DateTime, JSON, ForeignKey, func
from sqlalchemy import Column, String, Text, Integer, DateTime, JSON, ForeignKey, Index, func
from sqlalchemy.dialects.mysql import CHAR
from sqlalchemy.orm import relationship
from app.core.database import Base
@@ -34,6 +34,7 @@ class Execution(Base):
depth = Column(Integer, default=0, nullable=False, comment="执行深度根为0")
pause_state = Column(JSON, nullable=True, comment="挂起快照(审批节点 HITL恢复时消费")
goal_id = Column(CHAR(36), ForeignKey("goals.id"), nullable=True, comment="关联目标ID")
workspace_id = Column(CHAR(36), ForeignKey("workspaces.id"), nullable=True, comment="所属工作区ID")
created_at = Column(DateTime, default=func.now(), comment="创建时间")
# 关系

View File

@@ -1,7 +1,7 @@
"""
执行日志模型
"""
from sqlalchemy import Column, String, Text, Integer, DateTime, JSON, ForeignKey, func
from sqlalchemy import Column, String, Text, Integer, DateTime, JSON, ForeignKey, Index, func
from sqlalchemy.dialects.mysql import CHAR
from sqlalchemy.orm import relationship
from app.core.database import Base
@@ -22,6 +22,13 @@ class ExecutionLog(Base):
timestamp = Column(DateTime, default=func.now(), comment="时间戳")
duration = Column(Integer, comment="执行耗时(ms)")
__table_args__ = (
Index("idx_exec_execution_id", "execution_id"),
Index("idx_exec_timestamp", "timestamp"),
Index("idx_exec_level", "level"),
Index("idx_exec_node_id", "node_id"),
)
# 关系
execution = relationship("Execution", backref="logs")

View File

@@ -0,0 +1,18 @@
"""FCM / 设备推送 Token 模型"""
from sqlalchemy import Column, String, DateTime, func
from sqlalchemy.dialects.mysql import CHAR
from app.core.database import Base
import uuid
class FcmToken(Base):
"""设备推送 TokenFirebase Cloud Messaging / APNs"""
__tablename__ = "fcm_tokens"
id = Column(CHAR(36), primary_key=True, default=lambda: str(uuid.uuid4()))
user_id = Column(CHAR(36), nullable=False, index=True, comment="关联用户ID")
token = Column(String(512), nullable=False, unique=True, comment="FCM/APNs 设备令牌")
platform = Column(String(16), nullable=False, default="android", comment="android / ios / web")
created_at = Column(DateTime, server_default=func.now(), comment="注册时间")
last_used_at = Column(DateTime, server_default=func.now(), onupdate=func.now(), comment="最后推送时间")

View File

@@ -1,7 +1,7 @@
"""
目标模型 — Main Agent 管理的顶层目标
"""
from sqlalchemy import Column, String, Text, Integer, Float, DateTime, JSON, ForeignKey, func
from sqlalchemy import Column, String, Text, Integer, Float, DateTime, JSON, ForeignKey, Index, func
from sqlalchemy.dialects.mysql import CHAR
from sqlalchemy.orm import relationship
from app.core.database import Base
@@ -29,6 +29,7 @@ class Goal(Base):
creator_id = Column(CHAR(36), ForeignKey("users.id"), nullable=False, comment="创建者ID")
main_agent_id = Column(CHAR(36), ForeignKey("agents.id"), nullable=True, comment="管理此目标的 Main Agent ID")
parent_goal_id = Column(CHAR(36), ForeignKey("goals.id"), nullable=True, comment="父目标ID支持目标嵌套")
workspace_id = Column(CHAR(36), ForeignKey("workspaces.id"), nullable=True, comment="所属工作区ID")
# 时间
started_at = Column(DateTime, comment="开始时间")

View File

@@ -12,6 +12,7 @@ from datetime import datetime
from sqlalchemy import Column, String, Text, Integer, DateTime, ForeignKey, Index
from sqlalchemy.dialects.mysql import JSON as MySQLJSON
from sqlalchemy.dialects.mysql import CHAR
from sqlalchemy.orm import relationship
from app.core.database import Base
@@ -25,6 +26,7 @@ class KnowledgeBase(Base):
name = Column(String(200), nullable=False, comment="知识库名称")
description = Column(Text, nullable=True, comment="描述")
user_id = Column(String(36), nullable=True, index=True, comment="创建者 ID")
workspace_id = Column(CHAR(36), ForeignKey("workspaces.id"), nullable=True, comment="所属工作区ID")
chunk_size = Column(Integer, default=500, comment="分块大小(字符数)")
chunk_overlap = Column(Integer, default=50, comment="分块重叠(字符数)")
doc_count = Column(Integer, default=0, comment="文档数量")

View File

@@ -12,6 +12,7 @@ class KnowledgeEntry(Base):
__tablename__ = "knowledge_entries"
id = Column(String(36), primary_key=True, default=lambda: str(uuid.uuid4()))
agent_id = Column(String(36), nullable=True, index=True, comment="所属 Agent IDNULL=全Agent共享")
title = Column(String(500), nullable=False, comment="知识标题(一句话概括)")
category = Column(String(30), nullable=False, index=True,
comment="类别: bug_fix/best_practice/workaround/optimization/insight")
@@ -44,7 +45,6 @@ class KnowledgeEntry(Base):
updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now, comment="更新时间")
__table_args__ = (
Index("ix_knowledge_entries_category", "category"),
Index("ix_knowledge_entries_active", "is_active"),
)
@@ -54,6 +54,7 @@ class KnowledgeEntry(Base):
def to_dict(self) -> dict:
return {
"id": self.id,
"agent_id": self.agent_id,
"title": self.title,
"category": self.category,
"tags": self.tags or [],

View File

@@ -1,7 +1,7 @@
"""
模型配置模型
"""
from sqlalchemy import Column, String, DateTime, ForeignKey, func
from sqlalchemy import Column, String, DateTime, ForeignKey, Index, func
from sqlalchemy.dialects.mysql import CHAR
from sqlalchemy.orm import relationship
from app.core.database import Base
@@ -19,11 +19,16 @@ class ModelConfig(Base):
api_key = Column(String(500), nullable=False, comment="API密钥加密存储")
base_url = Column(String(255), comment="API地址")
user_id = Column(CHAR(36), ForeignKey("users.id"), comment="所属用户ID")
workspace_id = Column(CHAR(36), ForeignKey("workspaces.id"), nullable=True, comment="所属工作区ID")
created_at = Column(DateTime, default=func.now(), comment="创建时间")
updated_at = Column(DateTime, default=func.now(), onupdate=func.now(), comment="更新时间")
# 关系
user = relationship("User", backref="model_configs")
__table_args__ = (
Index("ix_model_configs_workspace", "workspace_id"),
)
def __repr__(self):
return f"<ModelConfig(id={self.id}, name={self.name}, provider={self.provider})>"

View File

@@ -2,7 +2,7 @@
节点模板模型
用于管理和复用LLM节点的提示词模板
"""
from sqlalchemy import Column, String, Text, Integer, DateTime, JSON, ForeignKey, Boolean, func
from sqlalchemy import Column, String, Text, Integer, DateTime, JSON, ForeignKey, Boolean, Index, func
from sqlalchemy.dialects.mysql import CHAR
from sqlalchemy.orm import relationship
from app.core.database import Base
@@ -37,6 +37,12 @@ class NodeTemplate(Base):
created_at = Column(DateTime, default=func.now(), comment="创建时间")
updated_at = Column(DateTime, default=func.now(), onupdate=func.now(), comment="更新时间")
__table_args__ = (
Index("idx_nt_category", "category"),
Index("idx_nt_user_id", "user_id"),
Index("idx_nt_is_public", "is_public"),
)
# 关系
user = relationship("User", backref="node_templates")

View File

@@ -1,7 +1,7 @@
"""
编排模板模型 — 保存可视化 Agent 编排画布配置
"""
from sqlalchemy import Column, String, Text, JSON, DateTime, ForeignKey, func
from sqlalchemy import Column, String, Text, JSON, DateTime, ForeignKey, Index, func
from sqlalchemy.dialects.mysql import CHAR
from sqlalchemy.orm import relationship
from app.core.database import Base
@@ -18,10 +18,15 @@ class OrchestrationTemplate(Base):
nodes = Column(JSON, nullable=False, comment="编排节点含Agent配置")
edges = Column(JSON, nullable=False, comment="编排连线")
user_id = Column(CHAR(36), ForeignKey("users.id"), comment="创建者ID")
workspace_id = Column(CHAR(36), ForeignKey("workspaces.id"), nullable=True, comment="所属工作区ID")
created_at = Column(DateTime, default=func.now(), comment="创建时间")
updated_at = Column(DateTime, default=func.now(), onupdate=func.now(), comment="更新时间")
user = relationship("User", backref="orchestration_templates")
__table_args__ = (
Index("ix_orchestration_templates_workspace", "workspace_id"),
)
def __repr__(self):
return f"<OrchestrationTemplate(id={self.id}, name={self.name})>"

View File

@@ -0,0 +1,18 @@
"""浏览器推送订阅模型"""
from sqlalchemy import Column, String, Text, DateTime, func, JSON
from sqlalchemy.dialects.mysql import CHAR
from app.core.database import Base
import uuid
class PushSubscription(Base):
__tablename__ = "push_subscriptions"
id = Column(CHAR(36), primary_key=True, default=lambda: str(uuid.uuid4()))
user_id = Column(CHAR(36), nullable=True, index=True, comment="关联用户ID")
endpoint = Column(Text, nullable=False, comment="推送端点URL")
p256dh = Column(Text, nullable=False, comment="公钥")
auth = Column(Text, nullable=False, comment="认证密钥")
user_agent = Column(String(500), nullable=True, comment="设备UA")
created_at = Column(DateTime, server_default=func.now())
updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())

View File

@@ -0,0 +1,98 @@
"""
场景契约模型 — 统一 DSL 输入契约(目标/约束/产物/验收)
让不同模板复用统一输入格式,实现"场景可编程输入"
"""
from sqlalchemy import Column, String, Text, Integer, DateTime, JSON, ForeignKey, Index, func
from sqlalchemy.dialects.mysql import CHAR
from sqlalchemy.orm import relationship
from app.core.database import Base
import uuid
class SceneContract(Base):
"""场景契约表 — 定义标准输入契约"""
__tablename__ = "scene_contracts"
id = Column(CHAR(36), primary_key=True, default=lambda: str(uuid.uuid4()), comment="契约ID")
name = Column(String(100), nullable=False, comment="契约名称")
description = Column(Text, comment="契约描述")
# ── 核心 DSL 字段 ──
goal = Column(Text, nullable=False, comment="场景目标(一句话描述要达成什么)")
role = Column(Text, nullable=True, comment="Agent 扮演的角色/人设")
# 输入规范
input_description = Column(Text, nullable=True, comment="期望的输入描述")
input_schema = Column(JSON, nullable=True, comment="输入 JSON Schema 约束")
# 约束条件
constraints = Column(JSON, nullable=True, comment="行为约束列表")
forbidden_actions = Column(JSON, nullable=True, comment="禁止的操作列表")
required_tools = Column(JSON, nullable=True, comment="必需的工具列表")
# 产物定义
deliverables = Column(JSON, nullable=True, comment="期望产出物列表 [{name, format, description}]")
# 验收标准
acceptance_criteria = Column(JSON, nullable=True, comment="验收条件列表")
# 输出规范
output_schema = Column(JSON, nullable=True, comment="输出 JSON Schema 约束")
# Few-shot 示例
examples = Column(JSON, nullable=True, comment="示例列表 [{input, output}]")
# 元数据
category = Column(String(50), nullable=True, comment="分类")
tags = Column(JSON, nullable=True, comment="标签列表")
version = Column(Integer, default=1, comment="版本号")
is_public = Column(Integer, default=0, comment="是否公开: 0=私有 1=公开")
use_count = Column(Integer, default=0, comment="使用次数")
# 关联
user_id = Column(CHAR(36), ForeignKey("users.id"), nullable=True, comment="创建者ID")
workspace_id = Column(CHAR(36), ForeignKey("workspaces.id"), nullable=True, comment="所属工作区ID")
template_binding = Column(String(100), nullable=True, comment="绑定的场景模板ID可选")
created_at = Column(DateTime, default=func.now(), comment="创建时间")
updated_at = Column(DateTime, default=func.now(), onupdate=func.now(), comment="更新时间")
__table_args__ = (
Index("idx_sc_category", "category"),
Index("idx_sc_user_id", "user_id"),
Index("idx_sc_is_public", "is_public"),
Index("idx_sc_template_binding", "template_binding"),
)
user = relationship("User", backref="scene_contracts")
def __repr__(self):
return f"<SceneContract(id={self.id}, name={self.name})>"
def to_dict(self):
return {
"id": self.id,
"name": self.name,
"description": self.description,
"goal": self.goal,
"role": self.role,
"input_description": self.input_description,
"input_schema": self.input_schema,
"constraints": self.constraints or [],
"forbidden_actions": self.forbidden_actions or [],
"required_tools": self.required_tools or [],
"deliverables": self.deliverables or [],
"acceptance_criteria": self.acceptance_criteria or [],
"output_schema": self.output_schema,
"examples": self.examples or [],
"category": self.category,
"tags": self.tags or [],
"version": self.version,
"is_public": self.is_public,
"use_count": self.use_count,
"user_id": self.user_id,
"workspace_id": self.workspace_id,
"template_binding": self.template_binding,
"created_at": self.created_at.isoformat() if self.created_at else None,
"updated_at": self.updated_at.isoformat() if self.updated_at else None,
}

View File

@@ -1,7 +1,7 @@
"""
任务模型 — Goal 拆解的子任务
"""
from sqlalchemy import Column, String, Text, Integer, DateTime, JSON, Boolean, ForeignKey, func
from sqlalchemy import Column, String, Text, Integer, DateTime, JSON, Boolean, ForeignKey, Index, func
from sqlalchemy.dialects.mysql import CHAR
from sqlalchemy.orm import relationship
from app.core.database import Base
@@ -25,9 +25,13 @@ class Task(Base):
# 任务编排配置
task_config = Column(JSON, comment="编排配置: {orchestration_mode, agents:[], workflow_id, input_data}")
# 依赖关系
# 依赖关系(参考 Claude Code task_system 设计)
parent_task_id = Column(CHAR(36), ForeignKey("tasks.id"), nullable=True, comment="父任务ID")
depends_on = Column(JSON, default=list, comment="前置依赖任务ID列表")
depends_on = Column(JSON, default=list, comment="前置依赖任务ID列表 (blockedBy)")
blocks = Column(JSON, default=list, comment="被此任务阻塞的任务ID列表")
# 认领机制(参考 Claude Code claimTask
owner = Column(String(200), nullable=True, comment="认领此任务的 Agent 标识 (区别于 assigned_agent_id)")
# 执行结果
result = Column(JSON, comment="执行输出结果")
@@ -42,6 +46,7 @@ class Task(Base):
requires_approval = Column(Boolean, default=False, comment="是否需要人工审批")
approver_id = Column(CHAR(36), ForeignKey("users.id"), nullable=True, comment="审批人ID")
approval_status = Column(String(20), comment="审批状态: pending/approved/rejected")
workspace_id = Column(CHAR(36), ForeignKey("workspaces.id"), nullable=True, comment="所属工作区ID")
# 时间
started_at = Column(DateTime, comment="开始时间")

View File

@@ -0,0 +1,88 @@
"""
虚拟团队模型 — Team + TeamMember
用于将多个 Agent 组织为角色化的虚拟软件团队
"""
from sqlalchemy import Column, String, Text, Integer, DateTime, JSON, ForeignKey, Boolean, Index, func
from sqlalchemy.dialects.mysql import CHAR
from sqlalchemy.orm import relationship
from app.core.database import Base
import uuid
class Team(Base):
"""虚拟团队表"""
__tablename__ = "teams"
id = Column(CHAR(36), primary_key=True, default=lambda: str(uuid.uuid4()), comment="团队ID")
name = Column(String(100), nullable=False, comment="团队名称")
description = Column(Text, comment="团队描述")
workspace_id = Column(CHAR(36), ForeignKey("workspaces.id"), nullable=True, comment="所属工作区ID")
user_id = Column(CHAR(36), ForeignKey("users.id"), nullable=False, comment="创建者ID")
is_template = Column(Boolean, default=False, comment="是否为预置模板")
config = Column(JSON, nullable=True, comment="团队配置(协作模式等)")
status = Column(String(20), default="active", comment="状态: active/inactive")
created_at = Column(DateTime, default=func.now(), comment="创建时间")
updated_at = Column(DateTime, default=func.now(), onupdate=func.now(), comment="更新时间")
__table_args__ = (
Index("idx_team_workspace", "workspace_id"),
Index("idx_team_user_id", "user_id"),
)
members = relationship("TeamMember", back_populates="team", cascade="all, delete-orphan")
user = relationship("User", backref="teams")
def __repr__(self):
return f"<Team(id={self.id}, name={self.name})>"
def to_dict(self, include_members=False):
result = {
"id": self.id,
"name": self.name,
"description": self.description,
"workspace_id": self.workspace_id,
"user_id": self.user_id,
"is_template": self.is_template,
"config": self.config,
"status": self.status,
"created_at": self.created_at.isoformat() if self.created_at else None,
"updated_at": self.updated_at.isoformat() if self.updated_at else None,
}
if include_members:
result["members"] = [m.to_dict() for m in self.members] if self.members else []
return result
class TeamMember(Base):
"""团队成员表 — Agent 在团队中的角色映射"""
__tablename__ = "team_members"
id = Column(CHAR(36), primary_key=True, default=lambda: str(uuid.uuid4()), comment="成员ID")
team_id = Column(CHAR(36), ForeignKey("teams.id"), nullable=False, comment="团队ID")
agent_id = Column(CHAR(36), ForeignKey("agents.id"), nullable=False, comment="Agent ID")
role = Column(String(30), nullable=False, comment="角色: pm/developer/qa/designer/devops/custom")
position = Column(Integer, default=0, comment="排序位置")
is_lead = Column(Boolean, default=False, comment="是否为团队Leader")
created_at = Column(DateTime, default=func.now(), comment="创建时间")
__table_args__ = (
Index("idx_tm_team_id", "team_id"),
Index("idx_tm_agent_id", "agent_id"),
)
team = relationship("Team", back_populates="members")
agent = relationship("Agent", backref="team_memberships")
def __repr__(self):
return f"<TeamMember(team={self.team_id}, agent={self.agent_id}, role={self.role})>"
def to_dict(self):
return {
"id": self.id,
"team_id": self.team_id,
"agent_id": self.agent_id,
"role": self.role,
"position": self.position,
"is_lead": self.is_lead,
"created_at": self.created_at.isoformat() if self.created_at else None,
}

View File

@@ -1,7 +1,7 @@
"""
工具定义模型
"""
from sqlalchemy import Column, String, Text, JSON, DateTime, Boolean, ForeignKey, Integer, func
from sqlalchemy import Column, String, Text, JSON, DateTime, Boolean, ForeignKey, Integer, Index, func
from sqlalchemy.dialects.mysql import CHAR
from sqlalchemy.orm import relationship
from app.core.database import Base
@@ -26,13 +26,20 @@ class Tool(Base):
# 元数据
is_public = Column(Boolean, default=False, comment="是否公开")
status = Column(String(20), default="active", comment="状态: active/draft/deprecated")
tags = Column(JSON, comment="标签列表")
user_id = Column(CHAR(36), ForeignKey("users.id"), nullable=True, comment="创建者ID")
workspace_id = Column(CHAR(36), ForeignKey("workspaces.id"), nullable=True, comment="所属工作区ID")
use_count = Column(Integer, default=0, comment="使用次数")
created_at = Column(DateTime, default=func.now(), comment="创建时间")
updated_at = Column(DateTime, default=func.now(), onupdate=func.now(), comment="更新时间")
# 关系
user = relationship("User", backref="tools")
__table_args__ = (
Index("ix_tools_workspace", "workspace_id"),
)
def __repr__(self):
return f"<Tool(id={self.id}, name={self.name})>"

View File

@@ -1,7 +1,7 @@
"""
工作流模型
"""
from sqlalchemy import Column, String, Text, Integer, DateTime, JSON, ForeignKey, func
from sqlalchemy import Column, String, Text, Integer, DateTime, JSON, ForeignKey, Index, func
from sqlalchemy.dialects.mysql import CHAR
from sqlalchemy.orm import relationship
from app.core.database import Base
@@ -20,11 +20,16 @@ class Workflow(Base):
version = Column(Integer, default=1, comment="版本号")
status = Column(String(20), default="draft", comment="状态: draft/published/running/stopped")
user_id = Column(CHAR(36), ForeignKey("users.id"), comment="创建者ID")
workspace_id = Column(CHAR(36), ForeignKey("workspaces.id"), nullable=True, comment="所属工作区ID")
created_at = Column(DateTime, default=func.now(), comment="创建时间")
updated_at = Column(DateTime, default=func.now(), onupdate=func.now(), comment="更新时间")
# 关系
user = relationship("User", backref="workflows")
__table_args__ = (
Index("ix_workflows_workspace", "workspace_id"),
)
def __repr__(self):
return f"<Workflow(id={self.id}, name={self.name})>"

View File

@@ -1,7 +1,7 @@
"""
工作流模板市场模型
"""
from sqlalchemy import Column, String, Text, Integer, DateTime, JSON, ForeignKey, Boolean, Float, func
from sqlalchemy import Column, String, Text, Integer, DateTime, JSON, ForeignKey, Boolean, Float, Index, func
from sqlalchemy.dialects.mysql import CHAR
from sqlalchemy.orm import relationship
from app.core.database import Base
@@ -27,11 +27,16 @@ class WorkflowTemplate(Base):
rating_count = Column(Integer, default=0, comment="评分次数")
rating_avg = Column(Float, default=0.0, comment="平均评分")
user_id = Column(CHAR(36), ForeignKey("users.id"), nullable=False, comment="创建者ID")
workspace_id = Column(CHAR(36), ForeignKey("workspaces.id"), nullable=True, comment="所属工作区ID")
created_at = Column(DateTime, default=func.now(), comment="创建时间")
updated_at = Column(DateTime, default=func.now(), onupdate=func.now(), comment="更新时间")
# 关系
user = relationship("User", backref="shared_templates")
__table_args__ = (
Index("ix_workflow_templates_workspace", "workspace_id"),
)
ratings = relationship("TemplateRating", back_populates="template", cascade="all, delete-orphan")
favorites = relationship("TemplateFavorite", back_populates="template", cascade="all, delete-orphan")

View File

@@ -1,7 +1,7 @@
"""
工作流版本模型
"""
from sqlalchemy import Column, String, Text, Integer, DateTime, JSON, ForeignKey, func
from sqlalchemy import Column, String, Text, Integer, DateTime, JSON, ForeignKey, Index, func
from sqlalchemy.dialects.mysql import CHAR
from sqlalchemy.orm import relationship
from app.core.database import Base
@@ -24,6 +24,11 @@ class WorkflowVersion(Base):
created_at = Column(DateTime, default=func.now(), comment="创建时间")
comment = Column(Text, comment="版本备注")
__table_args__ = (
Index("idx_wv_workflow_id", "workflow_id"),
Index("idx_wv_status", "status"),
)
# 关系
workflow = relationship("Workflow", backref="versions")
creator = relationship("User", foreign_keys=[created_by])

View File

@@ -0,0 +1,61 @@
"""
工作区 (Workspace) 模型 — 多租户数据隔离
"""
from sqlalchemy import Column, String, Text, Integer, DateTime, JSON, ForeignKey, Index, func
from sqlalchemy.dialects.mysql import CHAR
from sqlalchemy.orm import relationship
from app.core.database import Base
import uuid
class Workspace(Base):
"""工作区/租户表"""
__tablename__ = "workspaces"
id = Column(CHAR(36), primary_key=True, default=lambda: str(uuid.uuid4()), comment="工作区ID")
name = Column(String(100), nullable=False, comment="工作区名称")
description = Column(Text, comment="描述")
avatar = Column(String(500), nullable=True, comment="Logo URL")
is_default = Column(Integer, default=0, comment="是否为默认工作区: 0=否 1=是")
owner_id = Column(CHAR(36), ForeignKey("users.id"), nullable=False, comment="所有者ID")
max_members = Column(Integer, default=50, comment="最大成员数")
settings = Column(JSON, nullable=True, comment="工作区设置")
status = Column(String(20), default="active", comment="状态: active/disabled/deleted")
created_at = Column(DateTime, default=func.now(), comment="创建时间")
updated_at = Column(DateTime, default=func.now(), onupdate=func.now(), comment="更新时间")
owner = relationship("User", backref="owned_workspaces")
memberships = relationship("WorkspaceMembership", back_populates="workspace", cascade="all, delete-orphan")
def to_dict(self):
return {
"id": self.id,
"name": self.name,
"description": self.description,
"avatar": self.avatar,
"is_default": bool(self.is_default),
"owner_id": self.owner_id,
"max_members": self.max_members,
"settings": self.settings,
"status": self.status,
"created_at": self.created_at.isoformat() if self.created_at else None,
"updated_at": self.updated_at.isoformat() if self.updated_at else None,
}
class WorkspaceMembership(Base):
"""工作区成员关联表"""
__tablename__ = "workspace_memberships"
id = Column(CHAR(36), primary_key=True, default=lambda: str(uuid.uuid4()), comment="关联ID")
workspace_id = Column(CHAR(36), ForeignKey("workspaces.id", ondelete="CASCADE"), nullable=False, comment="工作区ID")
user_id = Column(CHAR(36), ForeignKey("users.id", ondelete="CASCADE"), nullable=False, comment="用户ID")
role = Column(String(20), nullable=False, default="member", comment="角色: admin/member")
joined_at = Column(DateTime, default=func.now(), comment="加入时间")
workspace = relationship("Workspace", back_populates="memberships")
user = relationship("User", backref="workspace_memberships")
__table_args__ = (
Index("ix_ws_membership_workspace_user", "workspace_id", "user_id", unique=True),
)