ForcePilot/backend/package/yuxi/channels/contract/policy/config_schema.py
Kris 696f5f44ec chore: 完成多模块迭代优化与测试覆盖
这一批提交包含:
1. 配置项敏感字段标记与测试用例修复
2. 路由绑定乐观锁支持与静态路径校验
3. 微信WOC插件能力适配与新增单元测试
4. 多个适配器的接口对齐与测试补全
5. 新增定时任务清理处理器与依赖注入容器测试
6. 错误码体系扩展与整合测试
7. 删除临时验证脚本与代码清理
2026-07-08 12:58:43 +08:00

622 lines
22 KiB
Python
Raw 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.

"""配置 schema 元数据契约层定义。
定义配置字段的完整 schema 元数据(``CONFIG_SCHEMA``),包括键、类型、
是否必填、默认值、是否可热更新与约束,作为配置 schema 的唯一真相源
§13.1 配置分层 / §6.1 契约层职责)。适配器(如 ``RedisConfigAdapter``
与应用层(如 ``ConfigManager``)共享引用本模块,消除 schema 元数据
下沉至适配器层的 DRY 违反。
``HOT_RELOADABLE_KEYS`` / ``NON_HOT_RELOADABLE_KEYS`` 从 ``CONFIG_SCHEMA``
派生,供 ``ConfigManager`` 做热更新策略校验ADP-030
"""
from __future__ import annotations
from yuxi.channels.contract.dtos.config import ConfigField
# 配置 schema全部配置字段的完整元数据FR-37 配置热更新)。
# 40 个可热更新字段 + 13 个不可热更新字段,共 53 个。
# 字段顺序先可热更新hot_reloadable=True后不可热更新hot_reloadable=False
CONFIG_SCHEMA: tuple[ConfigField, ...] = (
# === 可热更新字段hot_reloadable=True===
ConfigField(
key="dm_policy",
type="json",
required=False,
default="allow",
hot_reloadable=True,
title="私信策略",
description="控制私信/群聊的自动回复与路由策略,如是否允许陌生人私信、是否自动创建会话等。",
category="消息策略",
),
ConfigField(
key="allow_from",
type="json",
required=False,
default=None,
hot_reloadable=True,
title="来源白名单",
description="允许向当前账户发起对话的对端白名单,未配置时不限制。",
category="访问控制",
),
ConfigField(
key="rate_limit",
type="json",
required=False,
default=None,
hot_reloadable=True,
title="消息限流",
description="控制账户级别的消息发送频率限制,防止滥用或触发渠道风控。",
category="限流与预算",
),
ConfigField(
key="bot_loop_budget",
type="json",
required=False,
default={"max_replies_per_hour": 20, "cooldown_seconds": 60},
hot_reloadable=True,
title="Bot 循环预算",
description=(
"Bot 循环预算配置,包含 max_replies_per_hour"
"每小时最大回复数≤0 表示不限制)和 cooldown_seconds冷却秒数"
),
category="限流与预算",
),
# FR-08~14 功能开关Task 30
ConfigField(
key="rich_message_enabled",
type="bool",
required=False,
default=False,
hot_reloadable=True,
title="富消息开关",
description="是否启用富媒体消息(卡片、按钮等)发送能力。",
category="功能开关",
),
ConfigField(
key="status_writeback_enabled",
type="bool",
required=False,
default=False,
hot_reloadable=True,
title="状态回写开关",
description="是否将渠道用户状态(在线/离开等)回写到知识库。",
category="功能开关",
),
ConfigField(
key="agent_prompt_enabled",
type="bool",
required=False,
default=False,
hot_reloadable=True,
title="Agent 提示开关",
description="是否在消息中附加 Agent 提示上下文。",
category="功能开关",
),
ConfigField(
key="channel_tools_enabled",
type="bool",
required=False,
default=False,
hot_reloadable=True,
title="渠道工具开关",
description="是否允许 Agent 调用渠道相关工具。",
category="功能开关",
),
# AC-22: 消息操作开关默认"开"FR-12
ConfigField(
key="message_ops_enabled",
type="bool",
required=False,
default=True,
hot_reloadable=True,
title="消息操作开关",
description="总开关:是否启用消息操作(反应、置顶、卡片更新等)。",
category="功能开关",
),
ConfigField(
key="streaming_enabled",
type="bool",
required=False,
default=False,
hot_reloadable=True,
title="流式响应开关",
description="是否启用流式(打字机效果)响应输出。",
category="功能开关",
),
ConfigField(
key="enable_typing",
type="bool",
required=False,
default=True,
hot_reloadable=True,
title="输入中提示开关",
description="是否向用户展示'正在输入'状态提示。",
category="功能开关",
),
ConfigField(
key="directory_enabled",
type="bool",
required=False,
default=True,
hot_reloadable=True,
title="目录服务开关",
description="是否启用渠道目录(用户/群组列表)查询能力。",
category="功能开关",
),
ConfigField(
key="command_enabled",
type="bool",
required=False,
default=True,
hot_reloadable=True,
title="命令处理开关",
description="是否启用以 command_prefix 开头的命令解析与处理。",
category="功能开关",
),
# FR-12 消息操作细粒度开关PRD §FR-12 配置项)。
# AC-22 默认行为:表情反应"开"、置顶"关"、卡片更新"关"。
ConfigField(
key="enable_reaction",
type="bool",
required=False,
default=True,
hot_reloadable=True,
title="表情反应开关",
description="是否允许对消息添加表情反应。",
category="功能开关",
),
ConfigField(
key="enable_pin",
type="bool",
required=False,
default=False,
hot_reloadable=True,
title="消息置顶开关",
description="是否允许置顶/取消置顶消息。",
category="功能开关",
),
ConfigField(
key="enable_card_update",
type="bool",
required=False,
default=False,
hot_reloadable=True,
title="卡片更新开关",
description="是否允许更新已发送的卡片消息。",
category="功能开关",
),
# FR-15 管理员命令权限白名单账户级JSON 数组,存放允许执行 ADMIN 权限命令的 peer_id
ConfigField(
key="admin_users",
type="json",
required=False,
default=None,
hot_reloadable=True,
title="管理员白名单",
description="允许执行 ADMIN 权限命令的 peer_id 列表,账户级配置。",
category="身份与权限",
),
# FR-29 入站提及策略账户级JSON含 allowed_implicit_kinds 列表,
# 控制哪些隐式提及类型被视为"已提及",未配置时默认空策略)
ConfigField(
key="mention_policy",
type="json",
required=False,
default=None,
hot_reloadable=True,
title="提及策略",
description="控制哪些隐式提及类型被视为'已提及',影响命令触发与会话路由。",
category="消息策略",
),
# FR-07 会话合并策略开关(默认禁用)
ConfigField(
key="merge_strategy_enabled",
type="bool",
required=False,
default=False,
hot_reloadable=True,
title="会话合并开关",
description="是否启用按策略自动合并会话。",
category="会话策略",
),
# FR-06 跨渠道身份策略(默认隔离,可选 association 开启跨渠道关联)
ConfigField(
key="cross_channel_identity_strategy",
type="str",
required=False,
default="isolation",
hot_reloadable=True,
title="跨渠道身份策略",
description="跨渠道身份关联策略isolation隔离或 association关联",
category="身份与权限",
),
# 可配置值Task 30
ConfigField(
key="command_prefix",
type="str",
required=False,
default="/",
hot_reloadable=True,
title="命令前缀",
description="命令触发前缀,默认为 '/'",
category="命令与交互",
),
ConfigField(
key="fence_ttl_seconds",
type="int",
required=False,
default=120,
hot_reloadable=True,
title="围栏 TTL",
description="对话围栏fence的默认过期时间",
category="命令与交互",
),
ConfigField(
key="streaming_min_chunk_interval_ms",
type="int",
required=False,
default=200,
hot_reloadable=True,
title="流式最小间隔",
description="流式响应相邻分块之间的最小间隔(毫秒)。",
category="流式与传输体验",
),
ConfigField(
key="streaming_ttl_seconds",
type="int",
required=False,
default=60,
hot_reloadable=True,
title="流式 TTL",
description="流式响应状态的最大保留时间(秒)。",
category="流式与传输体验",
),
ConfigField(
key="typing_ttl_ms",
type="int",
required=False,
default=10000,
hot_reloadable=True,
title="输入提示 TTL",
description="'正在输入'提示状态的最大持续时间(毫秒)。",
category="流式与传输体验",
),
ConfigField(
key="durability_policy_default",
type="str",
required=False,
default="required",
hot_reloadable=True,
title="默认持久化策略",
description="消息默认持久化策略required / best_effort / none。",
category="可靠性",
),
# FR-24 分阶段 ACK 策略PRD §FR-24
# 四阶段策略after_record / after_agent_dispatch / after_persist / manual
# 默认 after_agent_dispatch平衡可靠性与响应速度
ConfigField(
key="ack_policy",
type="str",
required=False,
default="after_agent_dispatch",
hot_reloadable=True,
title="ACK 策略",
description="分阶段 ACK 策略after_record / after_agent_dispatch / after_persist / manual。",
category="可靠性",
),
# 手动策略下插件忘记 ACK 时核心兜底超时(默认 60s自动 ACK。
ConfigField(
key="ack_fallback_timeout_seconds",
type="int",
required=False,
default=60,
hot_reloadable=True,
title="ACK 兜底超时",
description="手动 ACK 策略下,插件忘记 ACK 时核心自动 ACK 的超时时间(秒)。",
category="可靠性",
),
# 传输引擎配置
ConfigField(
key="transport.stall_timeout_ms",
type="int",
required=False,
default=120000,
hot_reloadable=True,
title="传输停滞超时",
description="传输流判定为停滞的超时时间(毫秒)。",
category="传输引擎",
),
ConfigField(
key="transport.backoff_schedule",
type="str",
required=False,
default="1,2,5,10,30",
hot_reloadable=True,
title="传输退避序列",
description="传输重试的退避时间序列,逗号分隔的秒数列表。",
category="传输引擎",
),
ConfigField(
key="transport.backoff_jitter",
type="float",
required=False,
default=0.2,
hot_reloadable=True,
title="传输退避抖动",
description="传输重试退避的随机抖动系数。",
category="传输引擎",
),
# FR-22 Outbox 重试策略OBX-POLICY-UPDATEJSON 格式:
# {"max_retry": int, "ttl_seconds": int, "retry_backoff_schedule": [int, ...]}
# 由 OutboxHandler.updateRetryPolicy 双写到 ConfigPortRedis
# 支持跨请求 / 跨进程最终一致性。
ConfigField(
key="outbox_retry_policy",
type="json",
required=False,
default=None,
hot_reloadable=True,
title="Outbox 重试策略",
description="出站消息重试策略,包含 max_retry / ttl_seconds / retry_backoff_schedule。",
category="可靠性",
),
# === 渠道用户与 Agent 交互链路优化Task 6/7/8===
# 身份解析置信度阈值账户级scope=ACCOUNT低于此阈值的解析结果视为
# 未命中,进入 channel_guest 身份生成路径。缺口补齐:原 _getThreshold
# 使用硬编码默认值,现统一收敛至 CONFIG_SCHEMA。
ConfigField(
key="identity_confidence_threshold",
type="float",
required=False,
default=0.5,
hot_reloadable=True,
title="身份置信度阈值",
description="身份解析结果低于此阈值时视为未命中,进入 channel_guest 生成路径。",
category="身份与权限",
),
# 新建 Agent 的默认渠道访问级别全局级scope=GLOBAL取值如
# "none" / "read" / "write" 等,由 Agent 创建流程读取。
ConfigField(
key="channel_access_default_level",
type="str",
required=False,
default="none",
hot_reloadable=True,
title="默认渠道访问级别",
description="新建 Agent 默认的渠道访问级别,如 none / read / write。",
category="身份与权限",
),
# 渠道访客身份置信度全局级scope=GLOBALchannel_guest 身份生成时
# 使用此置信度,低于 identity_confidence_threshold 以避免被误判为
# 已解析身份。
ConfigField(
key="channel_guest_confidence",
type="float",
required=False,
default=0.3,
hot_reloadable=True,
title="访客身份置信度",
description="channel_guest 身份生成时使用的置信度,应低于身份置信度阈值。",
category="身份与权限",
),
# 是否启用管理员手动绑定功能全局级scope=GLOBAL控制渠道用户与
# 统一身份的手动绑定入口是否可用。
ConfigField(
key="enable_channel_user_binding",
type="bool",
required=False,
default=True,
hot_reloadable=True,
title="手动绑定开关",
description="是否启用管理员手动绑定渠道用户与统一身份的功能入口。",
category="身份与权限",
),
# === 渠道账号运营流程优化Task 14契约层 CONFIG_SCHEMA 健康检查配置键)===
# 以下 2 个键均为渠道级scope=CHANNEL归属 ConfigPortRedis 热重载,
# CHANNEL 业务策略体系),不得声明在 app_config。
# 凭证失效计数阈值。健康检查探活返回 401/403 时计入凭证失效计数,
# 达阈值后调聚合根 markFailed。窗口外credential_failure_window_seconds
# 重置计数。
ConfigField(
key="credential_failure_threshold",
type="int",
required=False,
default=3,
hot_reloadable=True,
title="凭证失效阈值",
description="健康检查中凭证失效计数的阈值。",
category="健康检查",
),
# 凭证失效计数窗口(秒)。窗口外的失效计数被重置,避免历史故障永久影响账号状态。
ConfigField(
key="credential_failure_window_seconds",
type="int",
required=False,
default=300,
hot_reloadable=True,
title="凭证失效窗口",
description="凭证失效计数的滑动窗口时长(秒)。",
category="健康检查",
),
# 审计日志保留策略全局级scope=GLOBAL。JSON 格式:
# {"retention_days": int, "cleanup_interval_hours": int}
# 控制审计日志的保留天数与清理周期,由审计清理任务读取。
ConfigField(
key="audit_retention_policy",
type="json",
required=False,
default={"retention_days": 90, "cleanup_interval_hours": 24},
hot_reloadable=True,
title="审计保留策略",
description="审计日志保留天数与清理周期配置JSON 格式。",
category="审计与登录",
),
# 扫码登录重定向 URI全局级scope=GLOBAL。扫码登录成功后的前端跳转地址
# 为空时使用默认跳转行为。
ConfigField(
key="qr_login_redirect_uri",
type="str",
required=False,
default="",
hot_reloadable=True,
title="扫码登录跳转地址",
description="扫码登录成功后的前端跳转地址。",
category="审计与登录",
),
# === 不可热更新字段hot_reloadable=False===
ConfigField(
key="webhook_port",
type="int",
required=False,
default=None,
hot_reloadable=False,
title="Webhook 端口",
description="渠道 Webhook 服务监听端口。",
category="基础设施",
),
ConfigField(
key="webhook_secret",
type="str",
required=False,
default=None,
hot_reloadable=False,
title="Webhook 密钥",
description="渠道 Webhook 签名验证密钥,敏感字段。",
category="基础设施",
),
ConfigField(
key="tls_cert",
type="str",
required=False,
default=None,
hot_reloadable=False,
title="TLS 证书",
description="TLS 证书内容,敏感字段。",
category="基础设施",
),
ConfigField(
key="tls_key",
type="str",
required=False,
default=None,
hot_reloadable=False,
title="TLS 私钥",
description="TLS 私钥内容,敏感字段。",
category="基础设施",
),
ConfigField(
key="plugin_entry",
type="str",
required=False,
default=None,
hot_reloadable=False,
title="插件入口",
description="插件入口模块路径。",
category="基础设施",
),
ConfigField(
key="database_url",
type="str",
required=False,
default=None,
hot_reloadable=False,
title="数据库地址",
description="业务数据库连接地址,敏感字段。",
category="基础设施",
),
ConfigField(
key="redis_url",
type="str",
required=False,
default=None,
hot_reloadable=False,
title="Redis 地址",
description="Redis 连接地址,敏感字段。",
category="基础设施",
),
ConfigField(
key="transport.max_restart_attempts",
type="int",
required=False,
default=None,
hot_reloadable=False,
title="最大重启尝试",
description="传输进程异常后的最大重启次数。",
category="传输引擎",
),
ConfigField(
key="transport.graceful_shutdown_timeout_s",
type="float",
required=False,
default=10.0,
hot_reloadable=False,
title="优雅关闭超时",
description="传输进程优雅关闭的最大等待时间(秒)。",
category="传输引擎",
),
# === 渠道账号运营流程优化Task 3契约层 CONFIG_SCHEMA 凭证配置键)===
# 以下 3 个键均为账户级scope=ACCOUNT归属 ConfigPortRedis 热重载,
# CHANNEL 业务策略/toggles/TTLs 体系),不得声明在 app_config。
# 凭证包,敏感字段加密存储。由 CredentialService 唯一写入入口维护,
# 包含 bot_token/app_secret/user_access_token 等渠道认证凭证。
ConfigField(
key="credentials",
type="dict",
required=False,
default=None,
hot_reloadable=False,
sensitive=True,
title="渠道凭证包",
description="渠道认证凭证集合,敏感字段。",
category="凭证与登录态",
),
# 登录态unlogged/logging_in/logged_in/logout_failed。由 QrLoginService
# 通过 ConfigPort 维护,区分扫码会话状态与最终凭证落库状态。
ConfigField(
key="login_status",
type="str",
required=False,
default="unlogged",
hot_reloadable=False,
title="登录状态",
description="当前账户扫码登录状态。",
category="凭证与登录态",
),
# 凭证版本号,每次 rotateCredentials 自增。用于乐观锁控制与缓存失效判定。
ConfigField(
key="credential_version",
type="int",
required=False,
default=0,
hot_reloadable=False,
title="凭证版本",
description="凭证轮换版本号,用于乐观锁与缓存失效。",
category="凭证与登录态",
),
# FR17-P0-3 已应用迁移列表ACCOUNT 作用域target=channel_type 或
# {channel_type}:{account_id})。由 PluginLifecycleManager 在插件 onStart
# 成功后追加 manifest.version 标记配置已加载DoctorService 只读不写。
# hot_reloadable=False非业务策略启动期写入无需热更新。
# default=[] 让首次加载时 ConfigPort.get 返回空列表而非 NotFoundError
# 避免 PluginLifecycleManager 与 DoctorService 的 catch 块接不到异常。
ConfigField(
key="applied_migrations",
type="json",
required=False,
default=[],
hot_reloadable=False,
title="已应用迁移",
description="账户已应用的插件迁移版本列表。",
category="凭证与登录态",
),
)
# 可热更新的配置项键集合FR-37从 CONFIG_SCHEMA 派生。
HOT_RELOADABLE_KEYS = frozenset(f.key for f in CONFIG_SCHEMA if f.hot_reloadable)
# 不可热更新的配置项键集合FR-37从 CONFIG_SCHEMA 派生。
NON_HOT_RELOADABLE_KEYS = frozenset(f.key for f in CONFIG_SCHEMA if not f.hot_reloadable)
__all__ = ["CONFIG_SCHEMA", "HOT_RELOADABLE_KEYS", "NON_HOT_RELOADABLE_KEYS"]