| name | hugagent-backend-dev |
| description | HugAgentOS 后端开发规范。当需要新增或修改后端代码(API路由、服务层、数据库模型、MCP工具等)时使用此 skill, 确保代码风格、分层架构、错误处理、响应格式等与项目现有规范保持一致。 |
HugAgentOS 后端开发规范
本 skill 定义了 HugAgentOS 项目后端(FastAPI + SQLAlchemy + AgentScope 2.0)的开发规范与流程。
所有后端代码变更必须遵守以下规范。
文件索引
模板 (templates/)
| 文件 | 用途 |
|---|
route.py | API 路由模板(CRUD 全套) |
service.py | Service 层模板(业务逻辑 + 审计) |
repository.py | Repository 层模板(CRUD + 软删除) |
model.py | ORM 模型模板(字段、索引、约束) |
test.py | 测试模板(fixture + repo/service 测试) |
参考文档 (references/)
| 文件 | 内容 |
|---|
architecture.md | 架构图、分层职责、模块索引、请求流转 |
error-codes.md | 完整错误码表(2xxxx-5xxxx) |
api-envelope.md | API 响应信封格式与工具函数用法 |
脚本 (scripts/)
| 文件 | 用途 |
|---|
scaffold_feature.sh | 一键生成新功能骨架(路由+服务+测试) |
模板中 ${Feature} / ${feature} / ${table_name} 为占位符,使用时替换为实际名称。
1. 目录结构与分层架构
src/backend/
├── api/ # API 层(路由 + 中间件)
│ ├── app.py # FastAPI 实例、中间件注册(路由按注册表注册)
│ ├── deps.py # 依赖注入(认证、DB session)
│ ├── health.py # 健康检查端点
│ ├── schemas.py # 请求/响应 Pydantic 模型
│ ├── middleware/ # CORS、错误处理、日志中间件
│ └── routes/v1/ # 路由文件;__init__.py 是 CE_ROUTERS 注册表(单一真源)
├── core/ # 核心业务逻辑(15 子模块)
│ ├── agent_skills/ # 技能引擎:加载/注册/选择(SKILL.md 解析、{dir} 沙箱路径注入)
│ ├── artifacts/ # 生成物注册与下载
│ ├── auth/ # 认证/权限接缝(permissions_iface.py)
│ ├── chat/ # 聊天上下文、工具日志
│ ├── config/ # Settings dataclass + catalog 五件套 + mcp_config + runtime_env
│ ├── content/ # 内容块、文件解析
│ ├── db/ # engine + models/ 包(11 领域文件)+ repository/ 包
│ ├── infra/ # 异常、响应、日志、指标、限流、Redis
│ ├── kb/ # 自建知识库:分块、向量化、混合检索
│ ├── licensing/ # 版本/License:features.py(能力位+402)、manager.py(状态机)
│ ├── llm/ # Agent 工厂、中间件(middlewares.py)、MCP 池、offloader、tools/ 自研工具
│ ├── memory/ # 分层记忆(L1 画像 / L2 向量 / L3 图谱,mem0 底座)
│ ├── sandbox/ # 沙箱 provider:protocol.py + script_runner 等实现
│ ├── services/ # 高级业务服务(30+)
│ └── storage/ # 存储协议 + 实现(local)
├── orchestration/ # 流式编排:workflow.py、chat_run_executor.py、strategy.py、citations.py、
│ # memory_integration.py、batch_orchestrator.py、schedulers/、subagents/
├── prompts/ # 系统提示词装配(DB 版本池优先,prompt_text/ 文件兜底)
├── mcp_servers/ # 内置 MCP server(streamable-http 常驻 mcp 容器,端口真源 _ports.py)
├── skill_bundles/ # 技能资产:default/(内置)+ marketplace/(可安装)
├── scripts/ # 运维脚本(export_content / import_content 等)
├── tests/ # 测试
└── alembic/ # 数据库迁移
核心原则: 路由层(routes) → 服务层(services) → 仓库层(repository) → 数据库(models),禁止跨层调用。
2. API 路由规范
2.1 路由文件结构
每个路由文件遵循以下模板:
from fastapi import APIRouter, Depends, Query, status
from sqlalchemy.orm import Session
from pydantic import BaseModel, Field
from typing import Optional, List
from api.deps import get_current_user, get_db
from core.infra.responses import success_response, created_response, paginated_response
from core.infra.exceptions import ResourceNotFoundError, BadRequestError
from core.services.xxx_service import XxxService
router = APIRouter(prefix="/v1/xxx", tags=["Xxx"])
class CreateXxxRequest(BaseModel):
name: str = Field(..., description="名称", max_length=200)
metadata: Optional[dict] = Field(default_factory=dict)
def _item_to_dict(item) -> dict:
return {
"id": item.id,
"name": item.name,
"created_at": item.created_at.isoformat() if item.created_at else None,
}
@router.get(, summary=)
():
service = XxxService(db)
items, total, total_pages = service.list_items(user.user_id, page, page_size)
paginated_response(items=[_item_to_dict(i) i items], page=page, ...)
():
service = XxxService(db)
item = service.create(user.user_id, body.name, body.metadata)
created_response(data=_item_to_dict(item))
2.2 路由规则
- 前缀: 所有路由使用
/v1/ 前缀
- 认证: 端点用
Depends(get_current_user) 注入当前用户
- 数据库: 通过
Depends(get_db) 获取 Session
- 响应: 必须使用
success_response() / created_response() / paginated_response() 包装
- 分页: 使用
Query(default, ge=, le=) 验证分页参数
- 状态码: POST 创建用 201,DELETE 用 204 或返回 success_response
- 注册: 新路由文件必须注册进
api/routes/v1/__init__.py 的 CE_ROUTERS(二元组 ("模块名", "router"))——这是单一真源,api/app.py 按表自动注册,不要再手工 include_router()
3. 响应格式规范
所有 v1 端点返回统一信封:
{
"code": 10000,
"message": "Success",
"data": { ... },
"trace_id": "req_abc123",
"timestamp": 1710000000000
}
状态码约定
| 范围 | 含义 | 示例 |
|---|
| 10000-19999 | 成功 | 10000=成功, 10001=已创建 |
| 20000-29999 | 客户端错误 | 20001=参数错误 |
| 30000-39999 | 认证错误 | 30001=未认证, 30002=无权限 |
| 40000-49999 | 资源错误 | 40001=未找到 |
| 50000+ | 服务端错误 | 50001=内部错误 |
使用方式
from core.infra.responses import success_response, created_response, paginated_response, error_response
return success_response(data={"id": "123"})
return created_response(data={"id": "new_123"})
return paginated_response(items=items_list, page=1, page_size=20, total_items=100)
raise ResourceNotFoundError("chat", chat_id)
raise BadRequestError("参数无效")
4. 数据库模型规范
4.1 ORM 模型模板
from sqlalchemy import Column, String, Integer, Boolean, Text, TIMESTAMP, ForeignKey, Index, CheckConstraint
from sqlalchemy.dialects.postgresql import JSONB
from sqlalchemy.orm import relationship
from core.db.engine import Base
from datetime import datetime
class MyModel(Base):
__tablename__ = "my_table"
id = Column(String(64), primary_key=True)
user_id = Column(
String(64),
ForeignKey("users_shadow.user_id", ondelete="CASCADE"),
nullable=False,
)
title = Column(String(500), nullable=False, default="默认标题")
content = Column(Text)
count = Column(Integer, default=0)
is_active = Column(Boolean, default=True)
extra_data = Column("metadata", JSONB, default={})
deleted_at = Column(TIMESTAMP(timezone=True))
created_at = Column(TIMESTAMP(timezone=True), default=datetime.utcnow)
updated_at = Column(TIMESTAMP(timezone=True), default=datetime.utcnow, onupdate=datetime.utcnow)
user = relationship("UserShadow", back_populates="my_models")
__table_args__ = (
CheckConstraint("count >= 0", name=),
Index(, ),
Index(, ),
)
4.2 模型规则
- 时间字段统一用
TIMESTAMP(timezone=True),默认 datetime.utcnow
- 使用软删除 (
deleted_at) 而非物理删除
- 灵活数据用
JSONB 列
- 外键必须指定
ondelete="CASCADE"
- 必须添加合适的索引
- 模型定义在
core/db/models/ 包内的对应领域文件(admin / agent / artifact / automation / chat / config / identity / knowledge / logs / memory / project),并在包 __init__.py re-export
- 新模型必须创建 Alembic 迁移:
alembic revision --autogenerate -m "描述"
5. Repository 模式
from typing import Optional, List, Tuple, Dict, Any
from sqlalchemy.orm import Session
from sqlalchemy import desc
class MyModelRepository:
def __init__(self, db: Session):
self.db = db
def get_by_id(self, id: str) -> Optional[MyModel]:
return self.db.query(MyModel).filter(
MyModel.id == id,
MyModel.deleted_at.is_(None),
).first()
def list_by_user(self, user_id: str, page: int = 1, page_size: int = 20) -> Tuple[List[MyModel], int]:
query = self.db.query(MyModel).filter(
MyModel.user_id == user_id,
MyModel.deleted_at.is_(None),
)
total = query.count()
items = query.order_by(desc(MyModel.updated_at)).offset((page - 1) * page_size).limit(page_size).all()
return items, total
def () -> MyModel:
item = MyModel(**data)
.db.add(item)
.db.commit()
.db.refresh(item)
item
() -> [MyModel]:
item = .get_by_id()
item:
key, value data.items():
(item, key, value)
.db.commit()
.db.refresh(item)
item
() -> :
item = .get_by_id()
item:
item.deleted_at = datetime.utcnow()
.db.commit()
规则: 每个领域实体一个 Repository,放在 core/db/repository/ 包内的对应领域文件(agent / artifact / audit / catalog / chat / kb / team / user),查询必须过滤 deleted_at.is_(None)。
6. Service 层规范
class MyService:
def __init__(self, db: Session):
self.db = db
self.repo = MyModelRepository(db)
def create(self, user_id: str, title: str, metadata: dict = None) -> MyModel:
item = self.repo.create({
"id": f"item_{uuid.uuid4().hex[:16]}",
"user_id": user_id,
"title": title,
"extra_data": metadata or {},
})
return item
def ensure_item(self, id: str, user_id: str) -> Optional[MyModel]:
"""幂等操作:存在则返回,不存在则创建。"""
existing = self.repo.get_by_id(id)
if existing:
if existing.user_id != user_id:
return None
return existing
return self.create(user_id=user_id, ...)
规则:
- 一个业务领域一个 Service
- 构造函数接收
Session,内部创建 Repository
- 业务逻辑在 Service 层,不在 Route 层
- 权限校验在 Service 层
- ORM → dict 转换在 Route 层的辅助函数中
7. 异常处理规范
from core.infra.exceptions import AppException, BadRequestError, ResourceNotFoundError, AuthenticationError
raise BadRequestError("参数 name 不能为空")
raise ResourceNotFoundError("chat_session", chat_id)
raise AuthenticationError("Token 已过期")
class QuotaExceededError(AppException):
def __init__(self, message: str = "配额已用完"):
super().__init__(code=20010, message=message, status_code=429)
规则: 不要在路由中直接返回错误响应,统一通过抛异常 → 全局 handler 转换为信封格式。
8. 依赖注入
from api.deps import get_current_user, get_db
@router.get("")
async def list_items(
user: UserContext = Depends(get_current_user),
db: Session = Depends(get_db),
):
...
9. 配置管理
from core.config.settings import settings
auth_mode = settings.auth.mode
db_url = settings.db.url
is_prod = settings.server.is_prod
@dataclass(frozen=True)
class MySettings:
my_flag: bool = _bool(os.getenv("MY_FLAG", "false"))
my_url: str = os.getenv("MY_URL", "")
10. Pydantic 模型规范
from pydantic import BaseModel, Field, field_validator
class MyRequest(BaseModel):
name: str = Field(..., description="名称", min_length=1, max_length=200)
desc: Optional[str] = Field(None, description="描述")
tags: List[str] = Field(default_factory=list, description="标签")
@field_validator("name")
@classmethod
def validate_name(cls, v: str) -> str:
return v.strip()
11. 测试规范
import pytest
from fastapi.testclient import TestClient
@pytest.fixture
def db_session():
engine = create_engine("sqlite:///:memory:")
Base.metadata.create_all(engine)
session = sessionmaker(bind=engine)()
yield session
session.close()
def test_create_item(db_session):
service = MyService(db_session)
item = service.create(user_id="test_user", title="Test")
assert item.title == "Test"
assert item.user_id == "test_user"
12. 代码风格
- 格式化: Black(100 字符行宽)+ isort(black profile)约定;但存量代码并非 formatter-clean,不要对已有文件整体跑 black/isort(会淹没真实 diff),改动处手工对齐风格即可
- 类型检查: mypy(permissive mode)
- 命令:
make format / make lint / make type-check(format 仅用于全新文件)
- import 顺序: 标准库 → 第三方 → 项目内部(isort 自动排序)
- 函数命名: snake_case
- 类命名: PascalCase
- 常量: UPPER_SNAKE_CASE
13. Docker 开发流程
docker-compose up -d --build backend
docker-compose up -d --build backend frontend
docker-compose build --no-cache backend
docker-compose up -d backend
docker-compose logs -f backend
14. 数据库迁移
alembic revision --autogenerate -m "add xxx table"
alembic upgrade head
alembic downgrade -1
15. 新功能开发检查清单