124 lines
5.6 KiB
Python
124 lines
5.6 KiB
Python
"""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 内部实现与机密信息,用户侧入口只能按披露边界给出拒答和产品能力替代说明。",
|
||
),
|
||
}
|