aigroup-econ-mcp 1.4.3__py3-none-any.whl → 2.0.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. PKG-INFO +344 -322
  2. README.md +335 -320
  3. __init__.py +1 -1
  4. aigroup_econ_mcp-2.0.1.dist-info/METADATA +732 -0
  5. aigroup_econ_mcp-2.0.1.dist-info/RECORD +170 -0
  6. cli.py +4 -0
  7. econometrics/advanced_methods/modern_computing_machine_learning/__init__.py +30 -0
  8. econometrics/advanced_methods/modern_computing_machine_learning/causal_forest.py +253 -0
  9. econometrics/advanced_methods/modern_computing_machine_learning/double_ml.py +268 -0
  10. econometrics/advanced_methods/modern_computing_machine_learning/gradient_boosting.py +249 -0
  11. econometrics/advanced_methods/modern_computing_machine_learning/hierarchical_clustering.py +243 -0
  12. econometrics/advanced_methods/modern_computing_machine_learning/kmeans_clustering.py +293 -0
  13. econometrics/advanced_methods/modern_computing_machine_learning/neural_network.py +264 -0
  14. econometrics/advanced_methods/modern_computing_machine_learning/random_forest.py +195 -0
  15. econometrics/advanced_methods/modern_computing_machine_learning/support_vector_machine.py +226 -0
  16. econometrics/advanced_methods/modern_computing_machine_learning/test_all_modules.py +329 -0
  17. econometrics/advanced_methods/modern_computing_machine_learning/test_report.md +107 -0
  18. econometrics/causal_inference/__init__.py +66 -0
  19. econometrics/causal_inference/causal_identification_strategy/__init__.py +104 -0
  20. econometrics/causal_inference/causal_identification_strategy/control_function.py +112 -0
  21. econometrics/causal_inference/causal_identification_strategy/difference_in_differences.py +107 -0
  22. econometrics/causal_inference/causal_identification_strategy/event_study.py +119 -0
  23. econometrics/causal_inference/causal_identification_strategy/first_difference.py +89 -0
  24. econometrics/causal_inference/causal_identification_strategy/fixed_effects.py +103 -0
  25. econometrics/causal_inference/causal_identification_strategy/hausman_test.py +69 -0
  26. econometrics/causal_inference/causal_identification_strategy/instrumental_variables.py +145 -0
  27. econometrics/causal_inference/causal_identification_strategy/mediation_analysis.py +121 -0
  28. econometrics/causal_inference/causal_identification_strategy/moderation_analysis.py +109 -0
  29. econometrics/causal_inference/causal_identification_strategy/propensity_score_matching.py +140 -0
  30. econometrics/causal_inference/causal_identification_strategy/random_effects.py +100 -0
  31. econometrics/causal_inference/causal_identification_strategy/regression_discontinuity.py +98 -0
  32. econometrics/causal_inference/causal_identification_strategy/synthetic_control.py +111 -0
  33. econometrics/causal_inference/causal_identification_strategy/triple_difference.py +86 -0
  34. econometrics/distribution_analysis/__init__.py +28 -0
  35. econometrics/distribution_analysis/oaxaca_blinder.py +184 -0
  36. econometrics/distribution_analysis/time_series_decomposition.py +152 -0
  37. econometrics/distribution_analysis/variance_decomposition.py +179 -0
  38. econometrics/missing_data/__init__.py +18 -0
  39. econometrics/missing_data/imputation_methods.py +219 -0
  40. econometrics/nonparametric/__init__.py +35 -0
  41. econometrics/nonparametric/gam_model.py +117 -0
  42. econometrics/nonparametric/kernel_regression.py +161 -0
  43. econometrics/nonparametric/quantile_regression.py +249 -0
  44. econometrics/nonparametric/spline_regression.py +100 -0
  45. econometrics/spatial_econometrics/__init__.py +68 -0
  46. econometrics/spatial_econometrics/geographically_weighted_regression.py +211 -0
  47. econometrics/spatial_econometrics/gwr_simple.py +154 -0
  48. econometrics/spatial_econometrics/spatial_autocorrelation.py +356 -0
  49. econometrics/spatial_econometrics/spatial_durbin_model.py +177 -0
  50. econometrics/spatial_econometrics/spatial_regression.py +315 -0
  51. econometrics/spatial_econometrics/spatial_weights.py +226 -0
  52. econometrics/specific_data_modeling/micro_discrete_limited_data/README.md +164 -0
  53. econometrics/specific_data_modeling/micro_discrete_limited_data/__init__.py +40 -0
  54. econometrics/specific_data_modeling/micro_discrete_limited_data/count_data_models.py +311 -0
  55. econometrics/specific_data_modeling/micro_discrete_limited_data/discrete_choice_models.py +294 -0
  56. econometrics/specific_data_modeling/micro_discrete_limited_data/limited_dependent_variable_models.py +282 -0
  57. econometrics/statistical_inference/__init__.py +21 -0
  58. econometrics/statistical_inference/bootstrap_methods.py +162 -0
  59. econometrics/statistical_inference/permutation_test.py +177 -0
  60. econometrics/survival_analysis/__init__.py +18 -0
  61. econometrics/survival_analysis/survival_models.py +259 -0
  62. econometrics/tests/causal_inference_tests/__init__.py +3 -0
  63. econometrics/tests/causal_inference_tests/detailed_test.py +441 -0
  64. econometrics/tests/causal_inference_tests/test_all_methods.py +418 -0
  65. econometrics/tests/causal_inference_tests/test_causal_identification_strategy.py +202 -0
  66. econometrics/tests/causal_inference_tests/test_difference_in_differences.py +53 -0
  67. econometrics/tests/causal_inference_tests/test_instrumental_variables.py +44 -0
  68. econometrics/tests/specific_data_modeling_tests/test_micro_discrete_limited_data.py +189 -0
  69. econometrics//321/206/320/254/320/272/321/205/342/225/235/320/220/321/205/320/237/320/241/321/205/320/264/320/267/321/207/342/226/222/342/225/227/321/204/342/225/235/320/250/321/205/320/225/320/230/321/207/342/225/221/320/267/321/205/320/230/320/226/321/206/320/256/320/240.md +544 -0
  70. pyproject.toml +9 -2
  71. server.py +15 -1
  72. tools/__init__.py +75 -1
  73. tools/causal_inference_adapter.py +658 -0
  74. tools/distribution_analysis_adapter.py +121 -0
  75. tools/gwr_simple_adapter.py +54 -0
  76. tools/machine_learning_adapter.py +567 -0
  77. tools/mcp_tool_groups/__init__.py +15 -1
  78. tools/mcp_tool_groups/causal_inference_tools.py +643 -0
  79. tools/mcp_tool_groups/distribution_analysis_tools.py +169 -0
  80. tools/mcp_tool_groups/machine_learning_tools.py +422 -0
  81. tools/mcp_tool_groups/microecon_tools.py +325 -0
  82. tools/mcp_tool_groups/missing_data_tools.py +117 -0
  83. tools/mcp_tool_groups/nonparametric_tools.py +225 -0
  84. tools/mcp_tool_groups/spatial_econometrics_tools.py +323 -0
  85. tools/mcp_tool_groups/statistical_inference_tools.py +131 -0
  86. tools/mcp_tools_registry.py +13 -3
  87. tools/microecon_adapter.py +412 -0
  88. tools/missing_data_adapter.py +73 -0
  89. tools/nonparametric_adapter.py +190 -0
  90. tools/spatial_econometrics_adapter.py +318 -0
  91. tools/statistical_inference_adapter.py +90 -0
  92. tools/survival_analysis_adapter.py +46 -0
  93. aigroup_econ_mcp-1.4.3.dist-info/METADATA +0 -710
  94. aigroup_econ_mcp-1.4.3.dist-info/RECORD +0 -92
  95. {aigroup_econ_mcp-1.4.3.dist-info → aigroup_econ_mcp-2.0.1.dist-info}/WHEEL +0 -0
  96. {aigroup_econ_mcp-1.4.3.dist-info → aigroup_econ_mcp-2.0.1.dist-info}/entry_points.txt +0 -0
  97. {aigroup_econ_mcp-1.4.3.dist-info → aigroup_econ_mcp-2.0.1.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,318 @@
1
+ """
2
+ 空间计量经济学适配器
3
+ 将核心算法适配为MCP工具
4
+ """
5
+
6
+ from typing import List, Optional, Union, Dict, Tuple
7
+ import json
8
+ from pathlib import Path
9
+
10
+ from econometrics.spatial_econometrics import (
11
+ create_spatial_weights,
12
+ morans_i_test,
13
+ gearys_c_test,
14
+ local_morans_i,
15
+ spatial_lag_model,
16
+ spatial_error_model,
17
+ spatial_durbin_model,
18
+ geographically_weighted_regression,
19
+ SpatialWeightsResult,
20
+ MoranIResult,
21
+ GearysCResult,
22
+ LocalMoranResult,
23
+ SpatialRegressionResult,
24
+ SpatialDurbinResult,
25
+ GWRResult
26
+ )
27
+
28
+ from .output_formatter import OutputFormatter
29
+
30
+
31
+ def spatial_weights_adapter(
32
+ coordinates: Optional[List[Tuple[float, float]]] = None,
33
+ adjacency_matrix: Optional[List[List[int]]] = None,
34
+ weight_type: str = "queen",
35
+ k: int = 4,
36
+ distance_threshold: Optional[float] = None,
37
+ bandwidth: Optional[float] = None,
38
+ kernel_type: str = "triangular",
39
+ row_standardize: bool = True,
40
+ output_format: str = "json",
41
+ save_path: Optional[str] = None
42
+ ) -> str:
43
+ """空间权重矩阵适配器"""
44
+
45
+ # 调用核心算法
46
+ result: SpatialWeightsResult = create_spatial_weights(
47
+ coordinates=coordinates,
48
+ adjacency_matrix=adjacency_matrix,
49
+ weight_type=weight_type,
50
+ k=k,
51
+ distance_threshold=distance_threshold,
52
+ bandwidth=bandwidth,
53
+ kernel_type=kernel_type,
54
+ row_standardize=row_standardize
55
+ )
56
+
57
+ # 格式化输出
58
+ if output_format == "json":
59
+ json_result = json.dumps(result.dict(), ensure_ascii=False, indent=2)
60
+ if save_path:
61
+ OutputFormatter.save_to_file(json_result, save_path)
62
+ return f"分析完成!结果已保存到: {save_path}\n\n{json_result}"
63
+ return json_result
64
+ else:
65
+ # Markdown格式
66
+ formatted = f"""# 空间权重矩阵分析结果
67
+
68
+ {result.summary}
69
+
70
+ ## 详细信息
71
+ - 观测数量: {result.n_observations}
72
+ - 权重类型: {result.weight_type}
73
+ - 平均邻居数: {result.n_neighbors_mean:.2f}
74
+ - 邻居数范围: [{result.n_neighbors_min}, {result.n_neighbors_max}]
75
+ - 非零权重: {result.pct_nonzero:.2f}%
76
+ - 是否对称: {'是' if result.is_symmetric else '否'}
77
+ """
78
+ if save_path:
79
+ OutputFormatter.save_to_file(formatted, save_path)
80
+ return f"分析完成!结果已保存到: {save_path}\n\n{formatted}"
81
+ return formatted
82
+
83
+
84
+ def morans_i_adapter(
85
+ values: List[float],
86
+ neighbors: dict,
87
+ weights: Optional[dict] = None,
88
+ permutations: int = 999,
89
+ two_tailed: bool = True,
90
+ output_format: str = "json",
91
+ save_path: Optional[str] = None
92
+ ) -> str:
93
+ """Moran's I检验适配器"""
94
+
95
+ # 调用核心算法
96
+ result: MoranIResult = morans_i_test(
97
+ values=values,
98
+ neighbors=neighbors,
99
+ weights=weights,
100
+ permutations=permutations,
101
+ two_tailed=two_tailed
102
+ )
103
+
104
+ # 格式化输出
105
+ if output_format == "json":
106
+ json_result = json.dumps(result.dict(), ensure_ascii=False, indent=2)
107
+ if save_path:
108
+ OutputFormatter.save_to_file(json_result, save_path)
109
+ return f"分析完成!结果已保存到: {save_path}\n\n{json_result}"
110
+ return json_result
111
+ else:
112
+ formatted = f"""# Moran's I 空间自相关检验结果
113
+
114
+ {result.summary}
115
+
116
+ ## 统计量
117
+ - Moran's I: {result.moran_i:.4f}
118
+ - 期望值: {result.expected_i:.4f}
119
+ - Z统计量: {result.z_score:.4f}
120
+ - P值: {result.p_value:.4f}
121
+
122
+ ## 解释
123
+ {result.interpretation}
124
+ """
125
+ if save_path:
126
+ OutputFormatter.save_to_file(formatted, save_path)
127
+ return f"分析完成!\n\n{formatted}\n\n已保存到: {save_path}"
128
+ return formatted
129
+
130
+
131
+ def gearys_c_adapter(
132
+ values: List[float],
133
+ neighbors: dict,
134
+ weights: Optional[dict] = None,
135
+ permutations: int = 999,
136
+ output_format: str = "json",
137
+ save_path: Optional[str] = None
138
+ ) -> str:
139
+ """Geary's C检验适配器"""
140
+
141
+ result: GearysCResult = gearys_c_test(
142
+ values=values,
143
+ neighbors=neighbors,
144
+ weights=weights,
145
+ permutations=permutations
146
+ )
147
+
148
+ if output_format == "json":
149
+ json_result = json.dumps(result.dict(), ensure_ascii=False, indent=2)
150
+ if save_path:
151
+ OutputFormatter.save_to_file(json_result, save_path)
152
+ return f"分析完成!结果已保存到: {save_path}\n\n{json_result}"
153
+ return json_result
154
+ else:
155
+ formatted = f"""# Geary's C 空间自相关检验结果
156
+
157
+ {result.summary}
158
+ """
159
+ if save_path:
160
+ OutputFormatter.save_to_file(formatted, save_path)
161
+ return formatted
162
+
163
+
164
+ def local_moran_adapter(
165
+ values: List[float],
166
+ neighbors: dict,
167
+ weights: Optional[dict] = None,
168
+ permutations: int = 999,
169
+ significance_level: float = 0.05,
170
+ output_format: str = "json",
171
+ save_path: Optional[str] = None
172
+ ) -> str:
173
+ """局部Moran's I (LISA) 适配器"""
174
+
175
+ result: LocalMoranResult = local_morans_i(
176
+ values=values,
177
+ neighbors=neighbors,
178
+ weights=weights,
179
+ permutations=permutations,
180
+ significance_level=significance_level
181
+ )
182
+
183
+ if output_format == "json":
184
+ json_result = json.dumps(result.dict(), ensure_ascii=False, indent=2)
185
+ if save_path:
186
+ OutputFormatter.save_to_file(json_result, save_path)
187
+ return f"分析完成!结果已保存到: {save_path}\n\n{json_result}"
188
+ return json_result
189
+ else:
190
+ formatted = f"""# 局部Moran's I (LISA) 分析结果
191
+
192
+ {result.summary}
193
+ """
194
+ if save_path:
195
+ OutputFormatter.save_to_file(formatted, save_path)
196
+ return formatted
197
+
198
+
199
+ def spatial_regression_adapter(
200
+ y_data: List[float],
201
+ x_data: List[List[float]],
202
+ neighbors: dict,
203
+ weights: Optional[dict] = None,
204
+ feature_names: Optional[List[str]] = None,
205
+ model_type: str = "sar",
206
+ method: str = "ml",
207
+ output_format: str = "json",
208
+ save_path: Optional[str] = None
209
+ ) -> str:
210
+ """空间回归模型适配器"""
211
+
212
+ # 调用核心算法
213
+ if model_type.lower() == "sar":
214
+ result: SpatialRegressionResult = spatial_lag_model(
215
+ y_data=y_data,
216
+ x_data=x_data,
217
+ neighbors=neighbors,
218
+ weights=weights,
219
+ feature_names=feature_names,
220
+ method=method
221
+ )
222
+ elif model_type.lower() == "sem":
223
+ result: SpatialRegressionResult = spatial_error_model(
224
+ y_data=y_data,
225
+ x_data=x_data,
226
+ neighbors=neighbors,
227
+ weights=weights,
228
+ feature_names=feature_names,
229
+ method=method
230
+ )
231
+ elif model_type.lower() == "sdm":
232
+ result: SpatialDurbinResult = spatial_durbin_model(
233
+ y_data=y_data,
234
+ x_data=x_data,
235
+ neighbors=neighbors,
236
+ weights=weights,
237
+ feature_names=feature_names
238
+ )
239
+ else:
240
+ raise ValueError(f"不支持的模型类型: {model_type}")
241
+
242
+ # 格式化输出
243
+ if output_format == "json":
244
+ json_result = json.dumps(result.dict(), ensure_ascii=False, indent=2)
245
+ if save_path:
246
+ OutputFormatter.save_to_file(json_result, save_path)
247
+ return f"分析完成!结果已保存到: {save_path}\n\n{json_result}"
248
+ return json_result
249
+ else:
250
+ formatted = f"""# {result.model_type if hasattr(result, 'model_type') else 'SDM'} 空间回归模型结果
251
+
252
+ {result.summary}
253
+
254
+ ## 系数估计
255
+ """
256
+ # 确保所有结果都是列表类型
257
+ feature_names = list(result.feature_names) if hasattr(result.feature_names, '__iter__') else []
258
+ coefficients = list(result.coefficients) if hasattr(result.coefficients, '__iter__') else []
259
+ std_errors = list(result.std_errors) if hasattr(result.std_errors, '__iter__') else []
260
+ z_scores = list(result.z_scores) if hasattr(result.z_scores, '__iter__') else []
261
+ p_values = list(result.p_values) if hasattr(result.p_values, '__iter__') else []
262
+
263
+ # 使用最短的长度来避免索引错误
264
+ min_len = min(len(feature_names), len(coefficients), len(std_errors), len(z_scores), len(p_values))
265
+
266
+ for i in range(min_len):
267
+ name = feature_names[i]
268
+ coef = coefficients[i]
269
+ se = std_errors[i]
270
+ z = z_scores[i]
271
+ p = p_values[i]
272
+ sig = "***" if p < 0.01 else "**" if p < 0.05 else "*" if p < 0.10 else ""
273
+ formatted += f"- {name}: {coef:.4f} (SE: {se:.4f}, Z={z:.2f}, p={p:.4f}){sig}\n"
274
+
275
+ if save_path:
276
+ OutputFormatter.save_to_file(formatted, save_path)
277
+ return f"分析完成!\n\n{formatted}\n\n已保存到: {save_path}"
278
+ return formatted
279
+
280
+
281
+ def gwr_adapter(
282
+ y_data: List[float],
283
+ x_data: List[List[float]],
284
+ coordinates: List[Tuple[float, float]],
285
+ feature_names: Optional[List[str]] = None,
286
+ kernel_type: str = "bisquare",
287
+ bandwidth: Optional[float] = None,
288
+ fixed: bool = False,
289
+ output_format: str = "json",
290
+ save_path: Optional[str] = None
291
+ ) -> str:
292
+ """地理加权回归适配器"""
293
+
294
+ result: GWRResult = geographically_weighted_regression(
295
+ y_data=y_data,
296
+ x_data=x_data,
297
+ coordinates=coordinates,
298
+ feature_names=feature_names,
299
+ kernel_type=kernel_type,
300
+ bandwidth=bandwidth,
301
+ fixed=fixed
302
+ )
303
+
304
+ if output_format == "json":
305
+ # 使用model_dump替代弃用的dict方法
306
+ json_result = json.dumps(result.model_dump(), ensure_ascii=False, indent=2)
307
+ if save_path:
308
+ OutputFormatter.save_to_file(json_result, save_path)
309
+ return f"分析完成!结果已保存到: {save_path}\n\n{json_result}"
310
+ return json_result
311
+ else:
312
+ formatted = f"""# 地理加权回归 (GWR) 结果
313
+
314
+ {result.summary}
315
+ """
316
+ if save_path:
317
+ OutputFormatter.save_to_file(formatted, save_path)
318
+ return formatted
@@ -0,0 +1,90 @@
1
+ """
2
+ 统计推断技术适配器
3
+ 将核心算法适配为MCP工具
4
+ """
5
+
6
+ from typing import List, Optional
7
+ import json
8
+
9
+ from econometrics.statistical_inference import (
10
+ bootstrap_inference,
11
+ permutation_test,
12
+ BootstrapResult,
13
+ PermutationTestResult
14
+ )
15
+
16
+ from .output_formatter import OutputFormatter
17
+
18
+
19
+ def bootstrap_adapter(
20
+ data: List[float],
21
+ statistic_func: str = "mean",
22
+ n_bootstrap: int = 1000,
23
+ confidence_level: float = 0.95,
24
+ method: str = "percentile",
25
+ random_state: Optional[int] = None,
26
+ output_format: str = "json",
27
+ save_path: Optional[str] = None
28
+ ) -> str:
29
+ """Bootstrap推断适配器"""
30
+
31
+ result: BootstrapResult = bootstrap_inference(
32
+ data=data,
33
+ statistic_func=statistic_func,
34
+ n_bootstrap=n_bootstrap,
35
+ confidence_level=confidence_level,
36
+ method=method,
37
+ random_state=random_state
38
+ )
39
+
40
+ if output_format == "json":
41
+ json_result = json.dumps(result.dict(), ensure_ascii=False, indent=2)
42
+ if save_path:
43
+ OutputFormatter.save_to_file(json_result, save_path)
44
+ return f"分析完成!结果已保存到: {save_path}\n\n{json_result}"
45
+ return json_result
46
+ else:
47
+ formatted = f"""# Bootstrap推断结果
48
+
49
+ {result.summary}
50
+ """
51
+ if save_path:
52
+ OutputFormatter.save_to_file(formatted, save_path)
53
+ return formatted
54
+
55
+
56
+ def permutation_test_adapter(
57
+ sample_a: List[float],
58
+ sample_b: List[float],
59
+ test_type: str = "mean_difference",
60
+ alternative: str = "two-sided",
61
+ n_permutations: int = 10000,
62
+ random_state: Optional[int] = None,
63
+ output_format: str = "json",
64
+ save_path: Optional[str] = None
65
+ ) -> str:
66
+ """置换检验适配器"""
67
+
68
+ result: PermutationTestResult = permutation_test(
69
+ sample_a=sample_a,
70
+ sample_b=sample_b,
71
+ test_type=test_type,
72
+ alternative=alternative,
73
+ n_permutations=n_permutations,
74
+ random_state=random_state
75
+ )
76
+
77
+ if output_format == "json":
78
+ json_result = json.dumps(result.dict(), ensure_ascii=False, indent=2)
79
+ if save_path:
80
+ OutputFormatter.save_to_file(json_result, save_path)
81
+ return f"分析完成!结果已保存到: {save_path}\n\n{json_result}"
82
+ return json_result
83
+ else:
84
+ formatted = f"""# 置换检验结果
85
+
86
+ {result.summary}
87
+ """
88
+ if save_path:
89
+ OutputFormatter.save_to_file(formatted, save_path)
90
+ return formatted
@@ -0,0 +1,46 @@
1
+ """
2
+ 生存分析适配器 - 简化版本
3
+ 使用完全简化的生存分析模块,避免lifelines依赖
4
+ """
5
+
6
+ from typing import List, Optional
7
+ import json
8
+
9
+ from econometrics.survival_analysis.survival_models import (
10
+ cox_regression_simple,
11
+ CoxRegressionResult
12
+ )
13
+
14
+ from .output_formatter import OutputFormatter
15
+
16
+
17
+ def cox_regression_adapter_simple(
18
+ durations: List[float],
19
+ event_observed: List[int],
20
+ covariates: List[List[float]],
21
+ feature_names: Optional[List[str]] = None,
22
+ confidence_level: float = 0.95,
23
+ output_format: str = "json",
24
+ save_path: Optional[str] = None
25
+ ) -> str:
26
+ """Cox回归适配器 - 简化版本"""
27
+
28
+ result: CoxRegressionResult = cox_regression_simple(
29
+ durations=durations,
30
+ event_observed=event_observed,
31
+ covariates=covariates,
32
+ feature_names=feature_names,
33
+ confidence_level=confidence_level
34
+ )
35
+
36
+ if output_format == "json":
37
+ json_result = json.dumps(result.dict(), ensure_ascii=False, indent=2)
38
+ if save_path:
39
+ OutputFormatter.save_to_file(json_result, save_path)
40
+ return f"分析完成!结果已保存到: {save_path}\n\n{json_result}"
41
+ return json_result
42
+ else:
43
+ formatted = f"""# Cox比例风险模型\n\n{result.summary}"""
44
+ if save_path:
45
+ OutputFormatter.save_to_file(formatted, save_path)
46
+ return formatted