File size: 17,798 Bytes
816198f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
# 配置入口说明

## 1. 整体说明

目前所有的配置项统一成了一套明确的三层配置系统,并保留了原来的 `utils.configs` 使用入口,尽量不影响现有业务代码。

特性:

- 支持统一从自定义 JSON、环境变量、`utils/config.py` 读取配置。
- 尽量兼容现有代码中 `from utils.configs import XXX` 的写法。
- 降低后续新增、删除、排查配置项时的维护成本。

当前配置优先级如下:

1. 自定义 JSON 文件
2. 环境变量
3. `utils/config.py` 中的默认值

也就是说:**越靠前优先级越高**。

---

## 2. 配置相关文件总览

本次改动后,配置系统的核心文件如下:

- `utils/config.py`
  - 默认配置定义文件。
  - 新增配置项时,首先应该在这里定义默认值。
- `utils/config_loader.py`
  - 配置加载核心逻辑。
  - 负责自动发现配置项、按优先级加载、做环境变量类型转换。
- `utils/configs.py`
  - 对外兼容层。
  - 旧代码仍然可以继续 `from utils.configs import XXX`- `utils/config/config.example.json`
  - JSON 配置样例。
- `utils/config/README.md`
  - 即当前文档,说明原理、使用方式和维护规范。

---

## 3. 整体工作流程

程序启动后,如果某处代码执行了:

```python
from utils.configs import CLIENT_TIMEOUT
```

或者:

```python
import utils.configs as configs
```

会触发如下链路:

1. Python 加载 `utils/configs.py`
2. `utils/configs.py` 再导入 `utils/config_loader.py`
3. `utils/config_loader.py` 在模块导入阶段创建 `_CONFIG_MANAGER = ConfigManager()`
4. `ConfigManager()` 内部加载 `utils.config`
5. 自动扫描 `utils.config` 中所有符合条件的“大写配置项”
6. 建立三层配置源:JSON、环境变量、默认 `config.py`
7. `utils.configs` 再把最终配置值暴露给业务代码

因此:

- **只要业务代码 import 了 `utils.configs`,这套配置逻辑就会启动**- **从使用视角看,只要在 `utils.configs` 可访问到一个新增的全大写配置变量,就可以直接通过 JSON / 环境变量配置后启动评测;按当前实现,这个变量的定义应新增在 `utils/config.py`,详细维护方式见后文的[开发者维护指南](#developer-guide)和[如何判断一个新配置是否会生效](#config-checklist)。**
- **配置项并不是手写白名单维护,而是自动从 `utils.config.py` 收集**。

---

## 4. 自动收集配置项的规则

当前系统不再手工维护 `CONFIG_KEYS` 白名单,而是从 `utils/config.py` 自动发现配置项。

自动发现规则如下:

一个变量会被当成“配置项”,必须同时满足:

- 变量名是全大写,例如 `CLIENT_TIMEOUT`
- 变量名**不能以下划线开头**,例如 `_SECRET` 不会被收集
- 变量值不能是可调用对象(callable),例如函数不会被收集

当前实现等价于下面这条规则:

```python
name.isupper() and not name.startswith("_") and not callable(value)
```

### 4.1 会被收集的例子

```python
CLIENT_TIMEOUT = 1800
LLM_SERVER_MODEL_NAME = ["demo_model"]
USE_NLP_FORMAT_RETURN = True
CHAT_PROFILE_CONFIGS = []
```

### 4.2 不会被收集的例子

```python
_client_timeout = 1800      # 以下划线开头,不收集
client_timeout = 1800       # 不是全大写,不收集
MixedCaseValue = 1          # 不是全大写,不收集

def BUILD_CONFIG():         # callable,不收集
    return {}
```

### 4.3 关于 `isupper()` 的注意事项

Python 的 `str.isupper()` 要求:

- 字母必须全部是大写
- 可以包含下划线和数字

所以这些名字都可以被收集:

```python
MODEL_V2_NAME = "a"
API_KEY_1 = "b"
LONG_REPORT_MAX_TOKENS = 4096
```

而这些不会被收集:

```python
Model_Name = "a"
api_key = "b"
LongReportMaxTokens = 4096
```

**建议统一使用 `UPPER_SNAKE_CASE` 命名。**

---

## 5. 三层配置优先级详解

### 5.1 第一层:JSON 配置(最高优先级)

系统会优先尝试读取 JSON 配置。

支持两种方式指定 JSON:

#### 方式 A:通过环境变量显式指定 JSON 路径

支持以下环境变量名:

- `S1_DR_CONFIG_JSON`
- `DR_SKILLS_CONFIG_JSON`
- `CONFIG_JSON_PATH`

示例:

```bash
export S1_DR_CONFIG_JSON=/path/to/config.json
```

如果给的是相对路径,例如:

```bash
export S1_DR_CONFIG_JSON=tmp/my_config.json
```

它会被解释为**相对于仓库根目录**的路径,而不是相对于任意 shell 当前目录。

#### 方式 B:不显式指定,使用默认搜索路径

如果没有显式设置上述环境变量,系统会按顺序查找以下文件:

- `config.local.json`
- `config.json`
- `utils/config/config.local.json`
- `utils/config/config.json`

注意:

- 搜索到第一个存在的文件后就会停止。
- 不会把多个 JSON 合并。
- 当前实现是“找到第一个有效 JSON 文件并加载”。

#### JSON 文件格式要求

JSON 顶层必须是一个对象(object),例如:

```json
{
  "CLIENT_TIMEOUT": 3600,
  "USE_NLP_FORMAT_RETURN": false
}
```

下面这种格式是错误的,因为顶层是数组:

```json
[
  {"CLIENT_TIMEOUT": 3600}
]
```

如果顶层不是对象,程序会抛出异常。

#### JSON 中的 key 命名要求

为了让 JSON 配置真正起作用,建议:

- key 与 `utils/config.py` 中的配置变量名保持**完全一致**
- 推荐使用全大写的 `UPPER_SNAKE_CASE`

例如:

`utils/config.py` 中是:

```python
MY_NEW_FLAG = "default"
```

那 JSON 中应写:

```json
{
  "MY_NEW_FLAG": "json_value"
}
```

不要写成:

```json
{
  "my_new_flag": "json_value"
}
```

后者虽然 JSON 能被读取,但因为业务代码不会访问 `my_new_flag`,通常不会产生你想要的效果。

### 5.2 第二层:环境变量

如果 JSON 没提供某个 key,系统会继续查环境变量。

对于任意配置项 `KEY`,系统会按顺序查找:

1. `KEY`
2. `S1_DR_KEY`
3. `DR_SKILLS_KEY`

例如配置项为:

```python
CLIENT_TIMEOUT = 1800
```

则会依次尝试:

- `CLIENT_TIMEOUT`
- `S1_DR_CLIENT_TIMEOUT`
- `DR_SKILLS_CLIENT_TIMEOUT`

先找到哪个,就使用哪个。

### 5.3 第三层:`utils/config.py` 默认值(最低优先级)

如果 JSON 和环境变量都没有给出某个配置项,就会回退到:

- `utils/config.py`

这是整个系统的默认值来源,也是自动发现配置项的来源。

---

## 6. 环境变量类型转换规则

环境变量本质上都是字符串,因此系统会根据 `utils/config.py` 里的默认值类型做自动转换。

这是一个很重要的细节:

- 类型推断**不是瞎猜**
- 而是参考 `config.py` 中该配置项的默认值类型

### 6.1 布尔值

如果默认值是 `bool`,支持以下写法:

```bash
export USE_NLP_FORMAT_RETURN=true
export USE_NLP_FORMAT_RETURN=false
export USE_NLP_FORMAT_RETURN=1
export USE_NLP_FORMAT_RETURN=0
export USE_NLP_FORMAT_RETURN=yes
export USE_NLP_FORMAT_RETURN=no
export USE_NLP_FORMAT_RETURN=on
export USE_NLP_FORMAT_RETURN=off
```

转换规则:

- `1`, `true`, `yes`, `on` -> `True`
- `0`, `false`, `no`, `off` -> `False`

大小写会先统一转成小写再判断。

### 6.2 整数

如果默认值是 `int`,会执行:

```python
int(raw_value)
```

例如:

```bash
export CLIENT_TIMEOUT=3600
```

### 6.3 浮点数

如果默认值是 `float`,会执行:

```python
float(raw_value)
```

### 6.4 列表

如果默认值是 `list`,优先按 JSON 解析;如果解析后不是列表,则退化成“逗号分隔”。

例如下面两种都可以:

```bash
export LLM_SERVER_MODEL_NAME='["model_a", "model_b"]'
```

或者:

```bash
export LLM_SERVER_MODEL_NAME=model_a,model_b
```

### 6.5 字典

如果默认值是 `dict`,那么环境变量值必须是合法 JSON 对象。

例如:

```bash
export SOME_DICT='{"a": 1, "b": 2}'
```

如果不是合法 JSON 对象,会抛出异常。

### 6.6 字符串

如果默认值不是上述类型,则保持原字符串。

---

## 7. 推荐使用方式

### 7.1 最推荐:默认值放 `config.py`,真实环境配置放 JSON

推荐原因:

- 真实环境配置可以与代码分离
- 便于本地、测试、线上使用不同 JSON
- 私有配置不必硬编码进仓库

推荐模式:

1.`utils/config.py` 里定义默认值
2. 在某个 JSON 文件中写真实覆盖值
3. 通过 `S1_DR_CONFIG_JSON` 指向该 JSON

例如:

```python
# utils/config.py
MY_NEW_FLAG = "default"
```

```json
{
  "MY_NEW_FLAG": "prod"
}
```

```bash
export S1_DR_CONFIG_JSON=/path/to/my_config.json
python your_eval_entry.py
```

### 7.2 适合临时调试:环境变量覆盖

例如只想临时改一个参数:

```bash
export CLIENT_TIMEOUT=7200
python your_eval_entry.py
```

这种方式适合:

- 临时实验
- CI 中临时注入参数
- shell 启动脚本里少量覆盖

不太适合:

- 结构复杂的长配置
- 多 profile 管理
- 团队共享配置模板

### 7.3 业务代码中的推荐 import 写法

如果你只是普通启动一次进程,下面两种都可以:

```python
from utils.configs import CLIENT_TIMEOUT
```

```python
import utils.configs as configs
print(configs.CLIENT_TIMEOUT)
```

但如果你希望在运行时使用 `reload_config()` 动态刷新,**更推荐第二种**```python
import utils.configs as configs

configs.reload_config()
print(configs.CLIENT_TIMEOUT)
```

因为:

```python
from utils.configs import CLIENT_TIMEOUT
```

在很多情况下会把值绑定为导入时的局部变量,后面即使 reload,当前模块中的这个局部名也不一定自动变化。

---

## 8. `reload_config()` 的用途和边界

`reload_config()` 的作用是:

- 重新读取 `utils.config`
- 重新自动发现大写配置项
- 重新扫描 JSON 配置路径
- 重新读取环境变量
- 刷新 `utils.configs` 模块中暴露的配置变量

典型场景:

- 你在长生命周期进程中临时改了环境变量
- 你切换了 `S1_DR_CONFIG_JSON`
- 你刚刚修改了 `utils/config.py`,想在当前解释器里重新生效

调用方式:

```python
import utils.configs as configs
configs.reload_config()
```

### 8.1 对“新增配置项”的效果

如果你新增了:

```python
NEW_SETTING = 123
```

然后执行:

```python
configs.reload_config()
```

那么新的配置项会被自动纳入配置系统。

### 8.2 对“删除配置项”的效果

如果你从 `utils/config.py` 删除了某个原有大写变量,调用 `reload_config()` 后:

- 它会从自动收集结果中消失
- `utils.configs` 中旧的同名全局变量也会被清理
- 再访问会触发 `AttributeError`

### 8.3 一个重要限制

如果其他模块已经这样写了:

```python
from utils.configs import CLIENT_TIMEOUT
```

并且这个 import 已经发生,那么该模块内的 `CLIENT_TIMEOUT` 很可能是导入时拷贝下来的值。

即使后面执行:

```python
configs.reload_config()
```

这个其他模块内部的局部变量也不一定自动更新。

这属于 Python import 机制本身的语义,不是本系统独有的问题。

所以:

- 对一次性启动的评测脚本:通常直接重启进程最稳妥
- 对需要热更新的场景:推荐统一使用 `import utils.configs as configs`

---

## 9. 常见使用示例

### 9.1 只用默认值

`utils/config.py````python
MY_BATCH_SIZE = 8
```

代码:

```python
from utils.configs import MY_BATCH_SIZE
print(MY_BATCH_SIZE)
```

输出:

```python
8
```

### 9.2 用环境变量覆盖

`utils/config.py````python
MY_BATCH_SIZE = 8
```

启动前:

```bash
export MY_BATCH_SIZE=32
```

程序读取到的是:

```python
32
```

### 9.3 用 JSON 覆盖

`utils/config.py````python
MY_BATCH_SIZE = 8
```

JSON:

```json
{
  "MY_BATCH_SIZE": 64
}
```

启动前:

```bash
export S1_DR_CONFIG_JSON=/path/to/config.json
```

程序读取到的是:

```python
64
```

### 9.4 JSON 优先级高于环境变量

如果:

```bash
export MY_BATCH_SIZE=32
export S1_DR_CONFIG_JSON=/path/to/config.json
```

并且 JSON 中:

```json
{
  "MY_BATCH_SIZE": 64
}
```

最终结果是:

```python
64
```

因为 JSON 优先级更高。

---

<a id="developer-guide"></a>

## 10. 开发者维护指南

这一节专门从开发者视角说明:后续怎样新增、删除、重构和排查配置项。

### 10.1 新增配置项的标准流程

假设你想新增一个配置项:`MY_NEW_FLAG`

#### 第一步:在 `utils/config.py` 中定义默认值

```python
MY_NEW_FLAG = "default_value"
```

命名建议:

- 使用 `UPPER_SNAKE_CASE`
- 不以下划线开头
- 不要定义成函数

#### 第二步:如果需要,更新示例文件

建议同步更新:

- `utils/config/config.example.json`
- 如果需要,也可以补充当前文档中的例子

例如:

```json
{
  "MY_NEW_FLAG": "example_value"
}
```

#### 第三步:在业务代码中读取

```python
from utils.configs import MY_NEW_FLAG
```

或者:

```python
import utils.configs as configs
print(configs.MY_NEW_FLAG)
```

#### 第四步:如果在当前进程内调试,记得 reload

```python
import utils.configs as configs
configs.reload_config()
```

#### 第五步:如果是正式脚本,最稳妥是重启进程

例如重新执行评测入口脚本,而不是依赖热更新。

### 10.2 删除配置项的标准流程

假设你想删除 `OLD_FLAG`

步骤如下:

1.`utils/config.py` 中删除 `OLD_FLAG`
2. 删除 JSON 示例中对应的条目
3. 全局搜索仓库中是否还有引用:

```bash
rg "OLD_FLAG" repo/s1-dr-skills-v3
```

4. 清理这些引用
5. 重新启动相关脚本,或在调试环境里执行 `reload_config()`

注意:

- 如果业务代码里还有 `from utils.configs import OLD_FLAG`,删除后会报错
- 这是预期行为,因为该配置项已经不存在了

### 10.3 修改已有配置项类型时的注意事项

例如你原来是:

```python
MY_SETTING = "1,2,3"
```

后来改成:

```python
MY_SETTING = [1, 2, 3]
```

这会影响环境变量的解析逻辑,因为环境变量类型推断依赖默认值类型。

因此修改配置项类型时,要同步检查:

- `config.example.json` 是否仍然合理
- 启动脚本中的环境变量是否还能正确解析
- 业务代码是否仍按新类型使用

### 10.4 不建议的做法

以下做法不推荐:

#### 不建议 1:在 `config.py` 中定义大量临时大写常量

因为所有符合规则的大写变量都会自动进入配置系统。

如果某个值只是模块内部常量,不希望变成“正式配置项”,不要写成会被自动收集的形式。

比如不要这样:

```python
TEMP_DEBUG_MARKER = "abc"
```

如果它并不是你想暴露给全仓库的配置。

可以改成:

```python
_temp_debug_marker = "abc"
```

或者:

```python
temp_debug_marker = "abc"
```

#### 不建议 2:依赖拼错 key 的 JSON 配置

例如 `config.py` 中是:

```python
CLIENT_TIMEOUT = 1800
```

但 JSON 写成:

```json
{
  "CLIENT-TIMEOUT": 3600
}
```

这种拼写不一致通常不会达到预期效果。

#### 不建议 3:把复杂结构随意塞进环境变量

环境变量适合少量覆盖,不太适合特别大的嵌套配置对象。

复杂对象更适合放 JSON。

### 10.5 推荐的维护原则

建议遵循以下原则:

- 默认值统一放 `utils/config.py`
- 真实环境配置优先放 JSON
- 临时实验用环境变量
- 配置名统一使用 `UPPER_SNAKE_CASE`
- 不以 `_` 开头,除非明确不想让它进入配置系统
- 修改配置项类型时,检查环境变量解析影响
- 删除配置项前,先全局搜索引用
- 需要热更新时,用 `import utils.configs as configs`

---

<a id="config-checklist"></a>

## 11. 如何判断一个新配置是否会生效

如果你新增了一个配置项,可以按这个 checklist 检查:

1. 它是否定义在 `utils/config.py`
2. 名字是否全大写
3. 是否没有以下划线开头
4. 是否不是函数或其他 callable
5. JSON 中的 key 是否与变量名完全一致
6. 业务代码是否真的读取了这个配置项
7. 是否是新启动的进程,或者已经执行了 `reload_config()`

只要这些条件都满足,它通常就会生效。

---

## 12. 排查问题时的建议

### 问题 1:为什么 JSON 配置没有生效?

排查顺序:

1. 是否真的设置了 `S1_DR_CONFIG_JSON`
2. 路径是否正确
3. JSON 是否是合法对象
4. JSON 中的 key 是否与 `config.py` 完全一致
5. 业务代码是否访问的是同一个名字
6. 是否其实被另一个更前面的 JSON 文件抢先匹配了

### 问题 2:为什么新增配置项没有生效?

排查顺序:

1. 变量名是不是全大写
2. 是否以下划线开头
3. 是否只是改了文件但没有重启进程
4. 是否需要调用 `reload_config()`
5. 业务代码是否真的 import/访问了该变量

### 问题 3:为什么删掉配置项后还能访问?

通常原因有两个:

1. 进程还没 reload / 重启
2. 某个模块之前已经 `from utils.configs import XXX`,把旧值绑定下来了

---

## 13. 一句话总结

当前配置系统的核心原则是:

- **配置项来源于 `utils/config.py` 中符合规则的大写变量**
- **最终取值遵循 `JSON > 环境变量 > config.py`**
- **对外统一从 `utils.configs` 访问**
- **新增/删除配置项主要维护 `utils/config.py`,并按需重启或 reload**

如果你后续继续维护这套系统,最重要的三条经验是:

1. 新配置名请用 `UPPER_SNAKE_CASE`
2. 不想暴露成配置项的变量,不要写成“全大写且不以下划线开头”
3. 动态变更配置时,优先理解 Python import 的静态绑定语义