policy.py 1.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566
  1. """Tool 调用策略:人格白名单、权限和副作用边界。"""
  2. from dataclasses import dataclass
  3. from typing import Literal
  4. from app.core.errors import AppError
  5. @dataclass(frozen=True)
  6. class ToolPolicy:
  7. personas: tuple[str, ...]
  8. effect: Literal["read", "draft"]
  9. required_permissions: tuple[str, ...] = ()
  10. class HarnessPolicyEngine:
  11. def can_use(
  12. self,
  13. *,
  14. persona: str,
  15. allowed_by_persona: tuple[str, ...],
  16. tool_name: str,
  17. policy: ToolPolicy,
  18. permissions: tuple[str, ...],
  19. ) -> bool:
  20. if tool_name not in allowed_by_persona or persona not in policy.personas:
  21. return False
  22. if persona == "operation" and policy.effect != "read":
  23. return False
  24. return not (
  25. policy.required_permissions
  26. and "*" not in permissions
  27. and not set(policy.required_permissions).intersection(permissions)
  28. )
  29. def authorize(
  30. self,
  31. *,
  32. persona: str,
  33. allowed_by_persona: tuple[str, ...],
  34. tool_name: str,
  35. policy: ToolPolicy,
  36. permissions: tuple[str, ...],
  37. ) -> None:
  38. if tool_name not in allowed_by_persona or persona not in policy.personas:
  39. raise AppError(
  40. "AGENT_TOOL_NOT_ALLOWED",
  41. f"当前Agent人格不能使用工具:{tool_name}",
  42. 403,
  43. )
  44. if persona == "operation" and policy.effect != "read":
  45. raise AppError(
  46. "AGENT_OPERATION_READ_ONLY",
  47. "运营Agent只允许执行只读工具",
  48. 403,
  49. )
  50. if (
  51. policy.required_permissions
  52. and "*" not in permissions
  53. and not set(policy.required_permissions).intersection(permissions)
  54. ):
  55. raise AppError(
  56. "PERMISSION_DENIED",
  57. f"缺少工具权限:{tool_name}",
  58. 403,
  59. )