PMFlow/backend/app/services/pmflow_ai_static_contract_r...

124 lines
5.6 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Static customer-safe contracts for PMFlow AI turn runtime.
Static contracts are not keyword routes. They are selected from canonical
TurnState frames after goal assessment has already decided the operation.
"""
from __future__ import annotations
from dataclasses import dataclass, field
from ..schemas.pmflow_ai_turn_runtime import PMFlowAITurnState, RuntimeActionKind
@dataclass(frozen=True)
class PMFlowAIStaticContract:
contract_key: str
authority_domain: str
required_evidence: str
renderer_key: str
verifier_policy: str
display_label: str
process_label: str
answer_step_label: str
required_claims: tuple[str, ...] = field(default_factory=tuple)
customer_safe_capabilities: tuple[str, ...] = field(default_factory=tuple)
source_summary: tuple[str, ...] = field(default_factory=tuple)
limitations: tuple[str, ...] = field(default_factory=tuple)
confidence: str = "high"
grounding_status: str = "grounded"
forbidden_first_actions: tuple[RuntimeActionKind, ...] = field(default_factory=tuple)
reason: str = ""
class PMFlowAIStaticContractRegistry:
"""Resolve static contracts from canonical turn state."""
def resolve_for_turn_state(self, turn_state: PMFlowAITurnState) -> PMFlowAIStaticContract | None:
disclosure = turn_state.disclosure_state
if (
disclosure.visibility_class == "protected_pmflow_internal"
or disclosure.allowed_response_mode == "protected_refusal"
):
return self.get(disclosure.contract_key or "protected_internal_refusal.v1")
if disclosure.contract_key:
contract = self.get(disclosure.contract_key)
if contract is not None:
return contract
selected = turn_state.selected_frame
if selected is None:
return None
if selected.operation_kind == "explain_assistant":
return self.get("assistant_identity.v1")
return None
def get(self, contract_key: str) -> PMFlowAIStaticContract | None:
return _STATIC_CONTRACTS.get(str(contract_key or "").strip())
_STATIC_CONTRACTS: dict[str, PMFlowAIStaticContract] = {
"assistant_identity.v1": PMFlowAIStaticContract(
contract_key="assistant_identity.v1",
authority_domain="pmflow_assistant_contract",
required_evidence="static_product_contract",
renderer_key="static_contract",
verifier_policy="static_contract",
display_label="PMFlow AI 产品能力边界",
process_label="说明 PMFlow AI 身份和能力边界",
answer_step_label="生成回答:按 PMFlow AI 身份与能力边界合同回答",
required_claims=(
"我是 PMFlow AI,是 PMFlow 里的项目操作副驾。",
"空间入口负责跨项目注意力、下一步、待确认事项、AI 进行中任务、暂存想法和可用外部信息。",
"进入具体项目后围绕项目真实状态、任务、风险、需求、执行记录、团队协作和项目记忆判断。",
"证据不足时不能把旧模板或页面零散信息冒充结论。",
),
customer_safe_capabilities=(
"查看当前空间里的项目、下一步、待确认事项和 AI 进行中任务",
"整理暂存想法,并在适合时进入项目启动或继续补证据",
"绑定具体项目后读取项目运行态证据,再判断进展、风险和下一步",
"需要外部公开资料时按来源证据回答",
),
source_summary=("PMFlow AI 产品能力边界",),
forbidden_first_actions=(
"inspect_surface_context",
"inspect_internal_projects",
"inspect_saved_ideas",
"resolve_project_objects",
"inspect_project_status",
"external_search",
"external_fetch",
),
reason="本轮目标是说明 PMFlow AI 的身份与能力边界,权威真源是客户安全产品合同,不需要项目或外部证据。",
),
"protected_internal_refusal.v1": PMFlowAIStaticContract(
contract_key="protected_internal_refusal.v1",
authority_domain="pmflow_internal_disclosure_boundary",
required_evidence="disclosure_policy",
renderer_key="static_contract",
verifier_policy="protected_refusal",
display_label="PMFlow 内部信息保护边界",
process_label="检查 PMFlow AI 信息披露边界",
answer_step_label="生成回答:按 PMFlow 内部信息保护合同回答",
required_claims=(
"用户侧入口不能披露 PMFlow 内部实现与机密信息。",
"不能提供技术架构、源码、数据库字段、prompt、模型供应商、内部路由、trace 或工具调用细节。",
"可以说明产品里可使用的客户可见能力边界。",
),
customer_safe_capabilities=(
"在空间入口查看项目、下一步、待确认事项、AI 进行中任务、暂存想法和可用外部信息",
"进入具体项目后围绕项目真实状态、任务、风险、需求、执行记录、团队协作和项目记忆继续判断",
),
source_summary=("PMFlow 内部信息保护边界",),
forbidden_first_actions=(
"inspect_surface_context",
"inspect_internal_projects",
"inspect_saved_ideas",
"resolve_project_objects",
"inspect_project_status",
"external_search",
"external_fetch",
),
reason="本轮请求触及 PMFlow 内部实现与机密信息,用户侧入口只能按披露边界给出拒答和产品能力替代说明。",
),
}