初始化提交
This commit is contained in:
commit
887c1f449e
963 files changed
+166869
No files matched your search
@@ -0,0 +1,105 @@
|
||||
"""安全服务:审计裁定、对话安全决策与司命策略加载。"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
import yaml
|
||||
from pydantic import BaseModel, Field
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.core.config import settings
|
||||
from app.db.session import AsyncSessionLocal
|
||||
from app.modules.safety.governor import SafetyGovernor
|
||||
from app.services.audit import record_audit_event
|
||||
|
||||
|
||||
async def allow_action(
|
||||
session: AsyncSession,
|
||||
*,
|
||||
action: str,
|
||||
character_id: str | None = None,
|
||||
run_id: str | None = None,
|
||||
reason: str = "当前阶段低风险动作默认允许。",
|
||||
) -> None:
|
||||
await record_audit_event(
|
||||
session,
|
||||
action="safety.decision",
|
||||
character_id=character_id,
|
||||
run_id=run_id,
|
||||
actor="system",
|
||||
risk_level="L1",
|
||||
decision="auto",
|
||||
reason=reason,
|
||||
input_summary=action,
|
||||
)
|
||||
|
||||
|
||||
class ChatSafetyDecision(BaseModel):
|
||||
"""对话安全决策结果(当前阶段默认低风险放行 + 基础安全约束)。"""
|
||||
|
||||
decision: str = "allow"
|
||||
risk_level: str = "L1"
|
||||
constraints: list[str] = Field(
|
||||
default_factory=lambda: ["不得泄露 API Key、Authorization header 或内部系统配置。"]
|
||||
)
|
||||
|
||||
|
||||
async def decide_chat_safety(
|
||||
session: AsyncSession,
|
||||
*,
|
||||
character_id: str,
|
||||
run_id: str,
|
||||
) -> ChatSafetyDecision:
|
||||
"""对话消息的安全决策:当前周期记录审计后默认放行。"""
|
||||
|
||||
await allow_action(
|
||||
session,
|
||||
action="chat.message.received",
|
||||
character_id=character_id,
|
||||
run_id=run_id,
|
||||
)
|
||||
return ChatSafetyDecision()
|
||||
|
||||
|
||||
def load_safety_governor(path: str | None = None) -> SafetyGovernor:
|
||||
"""从 YAML 加载司命工具策略;未配置或文件不存在时返回默认(空策略)Governor。
|
||||
|
||||
配置路径取自 ``path`` 参数或 ``settings.safety_policy_path``(即
|
||||
``SAFETY_POLICY_PATH`` 环境变量,示例见 ``config/safety.example.yaml``)。
|
||||
文件解析后交给 ``SafetyGovernor.from_policy_dict``
|
||||
(纯逻辑),本函数只承担文件 IO,governor 本身保持零 IO。
|
||||
|
||||
注:运行时主策略源为 tool_policies 表(见 load_governor_from_db / ToolRuntime.reload_governor);
|
||||
本函数保留为可选的 YAML 覆盖入口与遗留兼容。
|
||||
"""
|
||||
config_path = path or settings.safety_policy_path
|
||||
data: dict = {}
|
||||
if config_path:
|
||||
config_file = Path(config_path)
|
||||
if config_file.exists():
|
||||
with config_file.open(encoding="utf-8") as file:
|
||||
loaded = yaml.safe_load(file) or {}
|
||||
if isinstance(loaded, dict):
|
||||
data = loaded
|
||||
return SafetyGovernor.from_policy_dict(data)
|
||||
|
||||
|
||||
async def load_governor_from_db() -> SafetyGovernor:
|
||||
"""从 tool_policies 表加载司命策略(启动期主源)。
|
||||
|
||||
读取所有 enabled 策略,构造覆盖层 Governor;运行时写操作后改走
|
||||
``ToolRuntime.reload_governor`` 原地刷新,避免重建 ToolRuntime。
|
||||
"""
|
||||
from app.repositories import governance as governance_repo
|
||||
|
||||
async with AsyncSessionLocal() as session:
|
||||
policies = await governance_repo.list_tool_policies(session)
|
||||
data = {
|
||||
"tools": {
|
||||
p.name: {"risk_level": p.risk_level, "permission_policy": p.permission_policy}
|
||||
for p in policies
|
||||
if p.enabled
|
||||
}
|
||||
}
|
||||
return SafetyGovernor.from_policy_dict(data)
|
||||
Reference in new issue
Block a user