跳到主要内容

🛡️ Risk-MAS

基于多智能体系统的 ETF 投资组合风控框架

Python LangGraph License

基于 LangChain、LangGraph 构建的 ETF 风控多智能体系统(MAS),覆盖输入规范化、指标快照、编排调度与结论输出,支持接入宏观时序与政策、合规文本数据,产出可审计的风险结论与调仓建议。


📑 目录索引


📖 项目简介

Risk-MAS 是一个专为 ETF 投资组合设计的智能风控系统,采用多智能体架构(Multi-Agent System),将复杂的风控任务分解为多个专业化的分析模块,通过 LangGraph 实现高效的并行执行与智能调度。

设计理念

理念说明
确定性优先硬规则检查优先于 LLM 判断,确保关键风控逻辑的可靠性
并行高效6 个分析节点真正并行执行,大幅提升处理效率
可审计追溯完整的审计日志,所有决策都有据可查
灵活可配Skills 体系支持提示词、输出结构的热配置

🎓 学习路径

本项目定位为 MAS 教学项目,提供完整的学习资源:

资源说明适合人群
📖 README.md快速入门和使用指南用户:快速上手使用系统
📐 框架图说明_v2.md详细的架构文档,包含数据流图和模块交互说明开发者:深入理解系统设计
📘 PRACTICE.md10个模块的实战练习,包含代码模板和测试检查学习者:通过实践掌握 MAS 架构

💡 推荐学习路径:先阅读 README 了解系统 → 查看框架图说明理解架构 → 使用 PRACTICE.md 动手实践


✨ 核心特性

特性说明
🔄 并行执行基于 LangGraph Send API 实现 6 节点真正并行
📊 显式状态TypedDict 定义的 RiskState,类型安全可追溯
🔗 混合架构4 条确定性链 + 2 个 LLM Agent
🛠️ 工具白名单严格控制 LLM 可调用的工具范围,线程安全
⚖️ 智能决策硬规则优先 + 风险报告兜底,输出调仓建议
📝 Skills 体系提示词、输出结构、证据规则可配置化
🔍 审计追溯完整记录决策过程,支持复现与对账
🧮 约束求解CVXPY 生成满足约束的最优调仓方案

🏗️ 系统架构

架构概览

Risk-MAS 多智能体风控系统架构

工作流程

系统采用 DAG(有向无环图)编排,工作流程如下:

+--------------------------------------------------------------------------+
| INPUT LAYER |
| Intent + Context (Portfolio) |
+------------------------------------+-------------------------------------+
|
v
+--------------------------------------------------------------------------+
| PRE-PROCESS PIPELINE |
| +----------+ +--------------+ +----------+ +----------------+ |
| | Validate | > | Data Quality | > | Snapshot | > | Gatekeeper | |
| +----------+ +--------------+ +----------+ +----------------+ |
+------------------------------------+-------------------------------------+
|
v
+--------------------------------------------------------------------------+
| SUPERVISOR (LLM Router) |
| Selects nodes to run from candidates based on business logic |
+------------------------------------+-------------------------------------+
|
v
+--------------------------------------------------------------------------+
| PARALLEL ANALYSIS (6 Nodes Concurrent) |
| +--------+ +---------------+ +---------------+ +-----------+ |
| | Market | | Concentration | |Diversification| | Liquidity | |
| +--------+ +---------------+ +---------------+ +-----------+ |
| +------------------------+ +--------------------------------+ |
| | Macro (Agent) | | Compliance(Agent) | |
| +------------------------+ +--------------------------------+ |
+------------------------------------+-------------------------------------+
|
v
+--------------------------------------------------------------------------+
| POST-PROCESS PIPELINE |
| +---------+ +-------------+ +----------+ +--------+ +-------+ |
| | Reducer | > | Constraints | > | Decision | > | Solver | > | Audit | |
| +---------+ +-------------+ +----------+ +--------+ +-------+ |
+------------------------------------+-------------------------------------+
|
v
+--------------------------------------------------------------------------+
| OUTPUT LAYER |
| Decision + Recommendations + Audit Trail |
+--------------------------------------------------------------------------+
📋 工作流程中文说明
阶段英文名称中文说明关键节点
1️⃣INPUT LAYER输入层交易意图(Intent)+ 组合上下文(Context)
2️⃣PRE-PROCESS PIPELINE预处理管道验证 → 数据质量检查 → 指标快照 → 门控筛选
3️⃣SUPERVISOR智能调度器基于业务逻辑从候选节点中选择需要运行的节点
4️⃣PARALLEL ANALYSIS并行分析层6个风险分析节点并发执行(市场/集中度/分散度/流动性/宏观/合规)
5️⃣POST-PROCESS PIPELINE后处理管道汇总 → 约束评估 → 决策引擎 → 约束求解 → 审计日志
6️⃣OUTPUT LAYER输出层决策结果 + 调仓建议 + 审计追踪

关键特性:

  • 门控(Gatekeeper):基于数据可用性筛选候选节点
  • 调度器(Supervisor):基于业务逻辑智能选择节点
  • 并行执行:使用 LangGraph Send API 实现真正的并行
  • 约束求解:restrict 决策时自动生成满足约束的调仓方案

核心组件

分析节点说明

节点类型功能关键指标
Market确定性链市场风险评估组合波动率
Concentration确定性链集中度风险评估HHI 指数、最大权重
Diversification确定性链分散度风险评估有效持仓数(1/HHI)
Liquidity确定性链流动性风险评估买卖价差、成交量
MacroLLM Agent宏观经济分析利率、汇率、政策情绪
ComplianceLLM Agent合规风险分析禁投清单、政策合规

⚠️ Compliance 节点约束:要求先调用检索工具并在输出 evidence 中引用 rag:doc[i](硬约束)。

决策等级

等级Severity说明
Pass0放行,无风险
Warn1预警,建议关注
Restrict2限制,需调整
Block3阻断,禁止执行

📐 深入阅读:查看 框架图说明_v2.md 了解详细的架构设计、数据流图和模块间交互说明。


🚀 快速开始

30 秒快速体验

# 克隆并安装
git clone http://10.13.66.5:20091/Cufel-Practice/9-risk-mas.git && cd 9-risk-mas
uv sync

# 配置环境变量
touch .env
# 编辑 .env 填入 LLM API Key 等配置

# 运行
uv run --env-file .env -- python -u test.py

环境要求

  • Python 3.12+
  • uv(推荐)或 pip
  • 关键依赖:LangGraph >= 1.0、LangChain >= 1.2、CVXPY >= 1.7

安装步骤

# 使用 uv 安装依赖(推荐)
uv sync

# 或使用 pip
pip install -e .

配置说明

配置统一由 src/config.py 读取环境变量;大部分配置有默认值,可按需覆盖,按需在项目根目录创建 .env

变量名变量说明示例
OPENAI_BASE_URLLLM 兼容接口地址https://dashscope.aliyuncs.com/compatible-mode/v1
LLM_MODEL模型名称(不填则不启用 LLM)qwen3-max
OPENAI_API_KEYLLM API Keysk-*****
ENABLE_SUPERVISOR是否启用 LLM 调度1
TUSHARE_TOKENTushare Token(不填则宏观节点不进入候选)your_token
CSV_DATA_DIRCSV 数据目录cufel_practice_data
COMPLIANCE_RAG_SOURCE合规文本库(文件名或路径)csrc_2025.csv
RAG_ENGINE检索模式vector

完整配置项见 配置详解 章节。

运行方式

默认输出表格(推荐):

uv run --env-file .env -- python -u -m src.app

输出 JSON:

uv run --env-file .env -- python -u -m src.app -JSON --pretty

关闭 LLM 调度器:

ENABLE_SUPERVISOR=0 uv run --env-file .env -- python -u -m src.app

运行测试:

uv run --env-file .env -- python -u test.py

💡 test.py 会根据交易目标 ETF 的数量自动校准 rules.yaml


📘 使用指南

API 调用

from risk_mas import RiskMAS

# 初始化
mas = RiskMAS(output="table")

# 定义交易意图
intent = {
"date": "2025-11-15",
"mode": "target",
"targets": {
"159213": 0.25,
"159959": 0.25,
"511960": 0.20,
"516310": 0.20,
"561180": 0.10
}
}

# 定义上下文
context = {
"current_positions": {
"159213": 0.20,
"159959": 0.20,
"511960": 0.20,
"516310": 0.20,
"561180": 0.20
},
"policy_profile": "default",
"aum": 1000000.0
}

# 执行风控分析
result = mas.run(intent=intent, context=context)
print(result)

输入参数

Intent(交易意图)

字段必填类型说明
datestring交易意图日期,格式 YYYY-MM-DD
modestringtarget(目标权重)或 delta(增量调仓)
targetsdict目标/增量权重,{code: weight}

Context(组合上下文)

字段必填类型说明
current_positionsdict当前持仓 {code: weight}
current_positions_datestring当前持仓日期
universelist候选 ETF 池
policy_profilestring风险偏好:default / conservative
aumfloat组合 AUM
account_type / jurisdictionstring预留字段(用于将来分账户/分辖区规则)

RiskMAS 构造参数

参数默认值说明
llmNone可直接传入 LLM 实例(不传则按环境变量加载)
outputtabletablejson,决定返回表格还是 JSON 字符串
prettyFalseJSON 是否美化(带缩进/换行)
use_env_llmTrue是否从环境变量读取 LLM 配置
📋 点击展开数据质量口径
口径字段说明
marketmissing_etf_master / missing_marketETF 主表缺失、行情缺失
macrotimeseries_available是否具备宏观时序来源(TUSHARE_TOKEN
macrotext_available是否具备宏观文本来源(CSV)
macrolatest_date / freshness_days / freshness_status新鲜度口径
compliancetext_available合规文本是否可用
positionsfreshness_days当前持仓日期新鲜度

若宏观文本日期晚于 asof_date,会写入 data_gaps 为 block(防止未来信息泄露)

输出示例

📊 点击展开完整输出示例
----------------+---------------------------------------------------------------------------------------
KEY | VALUE
----------------+---------------------------------------------------------------------------------------
decision | restrict
recommendation | rebalance: 使用线性规划在约束下优化目标权重 | 159213=0.15906, 159959=0.42083, 511960=0.42011
llm_used | True
llm_model | qwen3-max
trace_id | eadf0c3f8f9353c5
----------------+---------------------------------------------------------------------------------------

-----------------+----------+-------------------
RISK | SEVERITY | SUMMARY
-----------------+----------+-------------------
market | 0 | 组合波动率处于目标范围
concentration | 0 | 组合集中度在合理范围内
diversification | 0 | 分散度较好
liquidity | 2 | 流动性较弱
compliance | 0 | 未发现合规问题
-----------------+----------+-------------------

---------------------------+----------------------------------------------------------------------------
AUDIT | VALUE
---------------------------+----------------------------------------------------------------------------
policy_profile | default
supervisor_result | market,concentration,diversification,liquidity,compliance
supervisor_rationale | 组合集中度指标显示HHI上升、有效持仓数下降,且最大个股权重较高,需运行concentration和diversification节点深入分析;weighted_adv和max_adv_ratio提示流动性风险,需运行liquidity节点;market节点用于提供基础市场上下文;macro节点因宏观风险severity为0且无异常信号可跳过;compliance为合规必需节点,应保留。
skills_used | 3
node_outputs | 35
tool_calls | 3
tool_errors | 0
compliance_blocklist_soft | none
compliance_industry_hits | none
---------------------------+----------------------------------------------------------------------------

--------------------------+-------------+------------+--------
RULE_KEY | VALUE | CURRENT | STATUS
--------------------------+-------------+------------+--------
adv_restrict | 7623.25713 | 5285.94543 | ⚠️
adv_warn | 13023.33612 | 5285.94543 | ⚠️
min_weighted_adv | 7623.25713 | 5285.94543 | ⚠️
blocklist | CCC | none | ✅
effective_n_restrict | 1.44431 | 2.63929 | ✅
effective_n_warn | 1.66426 | 2.63929 | ✅
hhi_restrict | 0.69237 | 0.37889 | ✅
hhi_warn | 0.60087 | 0.37889 | ✅
max_hhi | 0.69237 | 0.37889 | ✅
max_portfolio_volatility | 0.01936 | 0.00681 | ✅
max_single_weight | 0.81958 | 0.42083 | ✅
max_weighted_spread_bps | 244.75162 | 91.02152 | ✅
spread_restrict | 244.75162 | 91.02152 | ✅
spread_warn | 225.26680 | 91.02152 | ✅
top_weight_restrict | 0.81958 | 0.42083 | ✅
top_weight_warn | 0.74373 | 0.42083 | ✅
volatility_restrict | 0.01936 | 0.00681 | ✅
volatility_warn | 0.01764 | 0.00681 | ✅
--------------------------+-------------+------------+--------

------------+--------------+----------------------------------------+-----------------
AGENT | SUMMARY | EVIDENCE | RECOMMENDATIONS
------------+--------------+----------------------------------------+-----------------
compliance | 未发现合规问题 | tool:compliance_blocklist | 请复核合规规则
macro | 未运行 | 原因来自 supervisor_rationale | none
------------+--------------+----------------------------------------+-----------------

合规节点(JSON 片段示例,含 rag:doc[i] 引用):

{
"finding_compliance": {
"severity": 1,
"summary": "命中文本提示的行业限制,建议复核相关ETF。",
"evidence": [
{"ref": "rag:doc[1]", "quote": "…涉及行业限制的条款…"},
{"ref": "tool:policy_search", "hits": 3}
],
"recommendations": ["请核对禁投/限制清单并调整目标权重"]
}
}

⚙️ 配置详解

asof_date 取值规则

  • asof_date 会根据 intent.date 自动映射为"上一个交易日"(基于 CSV 数据中的交易日序列)
  • 该日期用于截断行情/宏观/合规数据,避免使用未来数据
  • 若找不到更早交易日,则回退到 intent.date

环境变量

环境变量由 RuntimeConfig 类统一读取并注入到关键模块。

数据与采样

变量默认值说明
CSV_DATA_DIRcufel_practice_dataCSV 数据目录
SAMPLE_UNIVERSE_SIZE5随机样本 ETF 数
RANDOM_SEED-随机种子
MARKET_LOOKBACK_DAYS60行情回溯天数

宏观时序

变量默认值说明
TUSHARE_TOKEN-Tushare Token(缺失则宏观节点不进入候选)
MACRO_SERIES_CONFIGcufel_practice_data/macro_series.yaml宏观指标配置路径
MACRO_STALE_DAYS30宏观文本数据陈旧阈值

合规文本 / RAG

变量默认值说明
COMPLIANCE_RAG_SOURCE-合规文本库(文件名或完整路径)
RAG_ENGINEvector检索模式:vector / keyword

💡 vector 模式需要 OPENAI_API_KEY;若未配置 embedding,可设置 RAG_ENGINE=keyword

组合执行与 LP

变量默认值说明
AUM / PORTFOLIO_AUM-组合 AUM
TARGET_HOLDINGS-调仓建议目标持仓数量
LP_TURNOVER_WEIGHT0.1LP 中换手惩罚权重
LP_SOLVER-LP 求解器名称(如 ECOS / OSQP

规则阈值

rules.yaml(组合规则阈值)

原理:

  • 在历史窗口内随机抽样组合:从 ETF 池随机抽取 n 只,使用 Dirichlet(1,...,1) 生成权重
  • 计算组合指标并形成经验分布(波动率、HHI、ADV、spread 等)
  • 波动率口径:优先使用复权价计算收益率与波动率
  • 对指标分布取分位数生成 warn/restrict 阈值

macro_series.yaml(宏观阈值配置)

原理:

  • 拉取宏观时序数据,并按 asof_date 截断
  • 计算相邻两期的变化幅度
  • 对变化幅度分布取分位数生成阈值

⚠️ 每次运行 test.py,阈值配置会根据用户输入(目标持仓的种类数、交易时间等)自动更新,会覆盖手动修改的值。

Skills 体系

Skills 体系将提示词、输出结构、工具权限、证据规则做成可配置的技能包:

技能说明工具
macro-tool-calling宏观工具调用代理macro_timeseries, macro_search
compliance-evidence合规证据代理policy_search, allowlist_check
risk-market-assessor市场风险评估无(确定性)
liquidity-execution-assessor流动性评估无(确定性)
supervisor-router监督路由

📁 项目结构

📂 点击展开精简结构
risk-mas/
├── src/ # 主源代码目录
│ ├── app.py # CLI 入口
│ ├── graph.py # LangGraph 编排
│ ├── state.py # 显式 State 定义
│ ├── config.py # 集中化配置管理
│ ├── skills_runtime.py # Skills 运行时
│ ├── chains/ # 分析链路
│ ├── agents/ # LLM Agent
│ └── tools/ # 确定性工具

├── cufel_practice_data/ # CSV 数据源
│ ├── rules.yaml # 规则阈值
│ └── *.csv # 数据文件

└── skills/ # Skills 配置
├── */SKILL.md # 技能定义
├── */output.schema.json # 输出 Schema
└── snippets/ # 可复用片段
📂 点击展开详细结构
risk-mas/
├── README.md # 项目说明
├── pyproject.toml # Python 依赖配置
├── uv.lock # 依赖锁定文件

├── src/ # 主源代码目录
│ ├── app.py # CLI 入口
│ ├── mas.py # RiskMAS 门面类
│ ├── graph.py # LangGraph 编排
│ ├── state.py # State 定义
│ ├── config.py # 配置管理
│ ├── skills_runtime.py # Skills 运行时
│ │
│ ├── chains/ # 分析链路
│ │ ├── common.py # 通用工具函数
│ │ ├── gatekeeper.py # 数据可用性检查与候选节点筛选
│ │ ├── supervisor.py # 业务逻辑节点选择
│ │ ├── market.py # 市场风险
│ │ ├── concentration.py # 集中度风险
│ │ ├── diversification.py # 分散度风险
│ │ ├── liquidity.py # 流动性风险
│ │ └── reducer.py # 结果聚合
│ │
│ ├── agents/ # LLM Agent
│ │ ├── agent_utils.py # 工具函数
│ │ ├── macro_agent.py # 宏观分析
│ │ └── compliance_agent.py # 合规分析
│ │
│ └── tools/ # 确定性工具
│ ├── utils.py # 共享工具函数
│ ├── validate.py # 输入验证
│ ├── data_quality.py # 数据质量
│ ├── snapshot.py # 指标快照
│ ├── constraints.py # 约束评估
│ ├── decision.py # 决策引擎
│ ├── solver.py # 约束求解
│ ├── audit.py # 审计日志
│ ├── rules.py # 规则加载
│ ├── csv_data.py # CSV 处理
│ ├── calibrate_rules.py # 规则校准
│ └── calibrate_macro_series.py # 宏观校准

├── cufel_practice_data/ # 数据源目录
│ ├── rules.yaml # 规则阈值配置
│ ├── macro_series.yaml # 宏观序列配置
│ ├── etf_2025_data.csv # ETF 行情数据
│ ├── sampled_etf_basic.csv # ETF 基础信息
│ ├── csrc_2025.csv # 合规文本库
│ ├── govcn_2025.csv # 宏观文本库
│ └── govcn_2025_results.json # 宏观情绪结果

└── skills/ # Skills 配置体系
├── macro-tool-calling/ # 宏观工具调用
├── compliance-evidence/ # 合规证据
├── risk-market-assessor/ # 市场风险评估
├── liquidity-execution-assessor/ # 流动性评估
├── supervisor-router/ # 监督路由
├── snippets/ # 可复用片段
├── schemas/ # 通用 Schema
└── tools/ # 工具接口定义

🔄 迁移指南

适配新数据源或资产类型时,按以下步骤操作:

1. 数据源与字段对齐

  • 按字段口径准备 CSV 文件
  • 若字段不一致,修改 src/tools/csv_data.py 的解析逻辑

2. 阈值与校准

  • 组合阈值:cufel_practice_data/rules.yaml
  • 宏观阈值:cufel_practice_data/macro_series.yaml

3. Skills 与工具权限

  • 调整 skills/*/SKILL.md 的 allowlist/snippets/schema
  • 确保 skills/tools/tool_interfaces.yaml 与实际工具一致

4. 审计与输出

  • 保持 audit 结构一致,便于对账与复现

❓ 常见问题

Q: 如何关闭 LLM 功能?

不设置 LLM_MODEL 环境变量,或设置 ENABLE_SUPERVISOR=0

Q: 宏观分析节点不运行?

检查是否设置了 TUSHARE_TOKEN。缺失时宏观节点不进入候选。

Q: 合规检索失败?
  1. 检查 COMPLIANCE_RAG_SOURCE 是否正确指向合规文本库
  2. 若 embedding 服务不可用,设置 RAG_ENGINE=keyword
Q: 如何自定义风险阈值?

注意:每次运行 test.py 会自动更新阈值配置,覆盖手动修改的值。

如需手动校准阈值(不运行 test.py 的情况下):

# 校准组合规则阈值
uv run --env-file .env -- python -u -m src.tools.calibrate_rules \
--asof-date <ASOF_DATE> --n <ETF数量>

# 校准宏观时序阈值
uv run --env-file .env -- python -u -m src.tools.calibrate_macro_series \
--asof-date <ASOF_DATE>

其中 <ASOF_DATE> 填交易意图日期的前一个交易日,<ETF数量> 填交易意图的 ETF 数量。


🔧 故障排查

问题可能原因解决方案
ModuleNotFoundError依赖未安装运行 uv syncpip install -e .
LLM 调用超时网络问题或 API 限流检查 OPENAI_BASE_URL,或设置 ENABLE_SUPERVISOR=0
数据文件找不到CSV_DATA_DIR 配置错误确认路径正确,默认为 cufel_practice_data
宏观节点被跳过缺少 TUSHARE_TOKEN配置 Token 或忽略(非必需)
合规检索返回空RAG 配置问题检查 COMPLIANCE_RAG_SOURCE,尝试 RAG_ENGINE=keyword
约束求解失败约束冲突或数据异常检查 rules.yaml 阈值是否合理

Risk-MAS - 让风控更智能、更可靠

GitHub

如有问题或建议,欢迎提交 Issue