T-0 / T-0b(门禁 · 2026-09-10) - T-0:sqlite 与 MySQL 结构对齐 —— core_holding 统一为 qty/cost_amount/as_of/pnl_pct + PK + UNIQUE(customer_id, product_id);补 core_product_nav;新增建库自校验 _assert_ddl_aligned()(R-g);test_db.py 增 3 条门禁用例(含反向验证门禁失效) - T-0b:DB 账号分离(D20)—— 新增 scripts/core/00-grant.sql(三账号逐表授权); settings.py 增 3 组账号;db.py 改 get_engine(db, role),缓存键改为 (库名, 角色), 账号未配置回退单账号;core_ro→ro / gateway_repository→rw / risk·session_repository→rw; tests/conftest.py 四处显式 role="admin"(R-e) T-1(数据层) - scripts/core/01-ddl.sql:新建 core_fee_rule / core_share_lot / core_convert_lot_detail; core_trade 加 convert_group_id + idx_convert_group;core_product 加 8 列 + fee_rate 补 COMMENT - 新增 07-seed-fee-rule.sql(赎回费 5 档 × 14 产品,按 22 号文 §10)/ 08-seed-share-lot.sql (58 行持仓 → 61 行批次,Σ remain_qty 恒等于 qty)/ 09-seed-org.sql(管理人 + TA + 申购费率 + 最低持有余额,v1.1 按「管理人全产品线」重排) - reset.ps1 追加 07/08/09;02-mysql-agent专用.sql 追加 risk_convert_detail - tests/_ddl.py 同步 4 表 + 新增 REQUIRED_CONVERT_TABLES 建库门禁 - 新增 scripts/dev/verify_convert_seed.py(pymysql 等价 reset 流程 + 8 条 DoD 断言, 含断言 ⑧「费率档 ↔ product_type 匹配」,越档即 FAIL) T-2 / T-2b(纯函数包 + 示例实算回填) - 新增 app/service/convert/ 7 文件:__init__ / types / calc / fee / nav / lot_bootstrap / errors (纯函数,不查库、不碰 SQL;所有量化显式 ROUND_HALF_UP;lot_bootstrap 用 zlib.crc32 保证 D18 跨进程同源) - 新增 tests/test_convert_calc.py 93 用例(12 类:HALF_UP 反向自证 / 分档边界 / FIFO 含同 confirmed_at 兜底 / 双口径 / 强制全转与强制赎回 / PRD §5.3 全链自证 / 纯函数零 IO 依赖断言) - 重写 scripts/dev/calc_convert_demo.py:去掉脚本内公式副本,改为调用生产 calc.py, 末尾与 PRD §5.3 逐项比对(不一致即退出码 1),兼作一致性门禁 验证 - pytest 609 passed / 3 skipped(516 → +93,零回归) - verify_convert_seed.py 8/8 PASS;calc_convert_demo.py 15/15 与 PRD §5.3 一致 文档:PRD v0.9.1(费率分类修正)· 架构 §7 签名回填 / §8.3 错误码注 / §15 T-2 完成 · 开发计划 §1.5 新增 R-h + §4.2·§4.3 执行记录 · AGENTS.md · docs/memory
95 lines
3.9 KiB
Python
95 lines
3.9 KiB
Python
"""环境配置(从 .env 读取,见 .env.example)。"""
|
||
|
||
from decimal import Decimal
|
||
|
||
from pydantic_settings import BaseSettings, SettingsConfigDict
|
||
|
||
|
||
class Settings(BaseSettings):
|
||
model_config = SettingsConfigDict(env_file=".env", env_file_encoding="utf-8", extra="ignore")
|
||
|
||
app_env: str = "development"
|
||
|
||
mysql_host: str = "127.0.0.1"
|
||
mysql_port: int = 3306
|
||
mysql_database: str = "jinrong_agent"
|
||
mysql_core_database: str = "jinrong_core"
|
||
mysql_user: str = "root"
|
||
mysql_password: str = ""
|
||
|
||
# ===== DB 账号分离(D20 · 架构设计-基金转换交易 §11.1)=====
|
||
# 把「Core 只读」「审计不可删改」从代码约定升级为 DB 级强制。
|
||
# 账号由 scripts/core/00-grant.sql 创建(管理员执行一次,不进 reset.ps1)。
|
||
# 留空 = 回退 mysql_user,行为与单账号时代完全一致(渐进启用,不阻塞本地开发)。
|
||
mysql_core_ro_user: str = ""
|
||
mysql_core_ro_password: str = ""
|
||
mysql_core_rw_user: str = ""
|
||
mysql_core_rw_password: str = ""
|
||
mysql_agent_user: str = ""
|
||
mysql_agent_password: str = ""
|
||
|
||
redis_url: str = "redis://127.0.0.1:6379/0"
|
||
|
||
neo4j_uri: str = "bolt://localhost:7687"
|
||
neo4j_user: str = "neo4j"
|
||
neo4j_password: str = ""
|
||
|
||
milvus_uri: str = "./data/milvus.db"
|
||
|
||
ollama_base_url: str = "http://127.0.0.1:11434"
|
||
embed_model: str = "bge-m3"
|
||
# 向量维度(必须与 Ollama bge-m3 输出一致;Milvus Collection 建集合时同源引用)
|
||
embed_dim: int = 1024
|
||
# Ollama embedding 单次请求超时(秒);本地推理首次加载模型可能较慢
|
||
embed_timeout_seconds: float = 60.0
|
||
|
||
deepseek_api_key: str = ""
|
||
deepseek_base_url: str = "https://api.deepseek.com"
|
||
|
||
# ===== JWT(T-01 · JWT 手册 §4/§11)=====
|
||
# RS256 公钥路径(生产,私钥仅在 IdP);为空时用 HS256 + jwt_dev_secret(仅 development)
|
||
jwt_public_key_path: str = ""
|
||
jwt_dev_secret: str = "change-me-in-dev-only"
|
||
jwt_issuer: str = "https://idp.jinrong.internal"
|
||
jwt_audience: str = "agent-gateway"
|
||
|
||
# ===== Risk 阈值(默认值=冻结规则 · docs/PRD/附-风控规则表.md)=====
|
||
# 注:risk_assessment_valid_days 已随 AL-05(对齐 main 基准)退役——
|
||
# 风评有效期改由 core_customer_risk.expires_at 数据驱动(FM-03),不再可配。
|
||
risk_large_amount: Decimal = Decimal("500000")
|
||
risk_daily_total: Decimal = Decimal("500000")
|
||
risk_freq_count: int = 3
|
||
risk_probe_window_minutes: int = 5
|
||
risk_probe_count: int = 3
|
||
risk_probe_amount: Decimal = Decimal("400000")
|
||
risk_small_amount: Decimal = Decimal("10000")
|
||
risk_small_count: int = 3
|
||
risk_aml_default_threshold: Decimal = Decimal("0.85")
|
||
|
||
# ===== 风控追加 v1.1(FR-8/9/10 · PRD §4A · C4~C6 共用,一次性加齐)=====
|
||
# FR-8 RISK-006 集中度:R4+R5 市值占比阈值(≥ 即命中)
|
||
risk_concentration_threshold: float = 0.80
|
||
# FR-9 RISK-007 时效升级:扫描周期(脚本侧参考)与两级超时小时数
|
||
risk_escalation_scan_minutes: int = 15
|
||
risk_escalation_l1_hours: int = 4
|
||
risk_escalation_l2_hours: int = 24
|
||
# AML 单走短通道(1h/4h),与普通单分开计
|
||
risk_escalation_aml_l1_hours: int = 1
|
||
risk_escalation_aml_l2_hours: int = 4
|
||
# FR-10 RISK-008 代理人行为链:扫描周期与 A/B/C 三条件窗口与次数
|
||
risk_agent_behavior_scan_minutes: int = 30
|
||
risk_agent_behavior_a_window_hours: int = 24
|
||
risk_agent_behavior_a_count: int = 3
|
||
risk_agent_behavior_b_window_hours: int = 72
|
||
risk_agent_behavior_b_count: int = 5
|
||
risk_agent_behavior_c_window_hours: int = 24
|
||
risk_agent_behavior_c_count: int = 10
|
||
|
||
# ===== 输入防护(T-03 · F-03)=====
|
||
# 对话限流:actor 级固定窗口(拍板 2026-09-07:30 次/分钟,Redis 异常 fail-open)
|
||
guard_rate_limit_max: int = 30
|
||
guard_rate_limit_window_seconds: int = 60
|
||
|
||
|
||
settings = Settings()
|