From bca1ceeea54f106ed328c0e86a58c41ef7aa0d60 Mon Sep 17 00:00:00 2001 From: yeuimu <2197651308@qq.com> Date: Wed, 2 Sep 2026 16:39:53 +0800 Subject: [PATCH] =?UTF-8?q?config:=20[run]=20verify=5Fclean=20=E5=8F=AF?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=E6=97=A0=E8=BF=9D=E8=A7=84=E4=B8=A4=E7=A5=A8?= =?UTF-8?q?=E5=A4=8D=E6=A0=B8=E5=BC=80=E5=85=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - config.ini [run] verify_clean = yes/no(默认 yes),GUI/CLI 均跟随 - CLI --no-verify 仍可强制关闭;非法值回落默认开启 - 模板/示例/README 同步,新增配置测试(共 36 个全部通过) --- README.md | 1 + config.example.ini | 2 ++ src/violation_detector/cli.py | 4 +++- src/violation_detector/config.py | 15 ++++++++++++++- src/violation_detector/gui.py | 2 +- tests/test_config.py | 14 ++++++++++++++ 6 files changed, 35 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 5ba8bac..c78dce6 100644 --- a/README.md +++ b/README.md @@ -78,6 +78,7 @@ model = deepseek-v4-flash-vision-exp max_tokens = 5000 [run] mode = cascade # cascade / doubao(仅豆包)/ deepseek(仅 DeepSeek) +verify_clean = yes # DeepSeek 判无违规后是否追加两票复核(yes/no) ``` 运行模式:config.ini `[run] mode` 全局生效(GUI 也跟随);CLI 的 `--mode` 参数可临时覆盖。 diff --git a/config.example.ini b/config.example.ini index d8b660e..5b84837 100644 --- a/config.example.ini +++ b/config.example.ini @@ -25,5 +25,7 @@ file = recheck = 无违规,违规不明,检测异常 # 运行模式:cascade=豆包初筛+DeepSeek复检(默认)/ doubao=仅豆包 / deepseek=仅DeepSeek +# 无违规两票复核:DeepSeek 判为无违规的图片是否再追加两票判定(yes/no,默认 yes) [run] mode = cascade +verify_clean = yes diff --git a/src/violation_detector/cli.py b/src/violation_detector/cli.py index bcd1b37..8480206 100644 --- a/src/violation_detector/cli.py +++ b/src/violation_detector/cli.py @@ -48,6 +48,8 @@ def main(argv=None) -> int: if args.max_tokens: cfg.max_tokens = args.max_tokens mode = args.mode or cfg.mode + # 复核开关:CLI --no-verify 强制关;否则跟随 config.ini [run] verify_clean + verify = cfg.verify_clean and not args.no_verify missing = missing_fields(cfg, mode) if missing: @@ -58,7 +60,7 @@ def main(argv=None) -> int: try: rows, summary = asyncio.run(run_detection( - args.folder, cfg, mode=mode, verify=not args.no_verify)) + args.folder, cfg, mode=mode, verify=verify)) except FileNotFoundError as e: logger.error("路径或文件不存在:%s", e) return 1 diff --git a/src/violation_detector/config.py b/src/violation_detector/config.py index 3ecbc03..924c8d3 100644 --- a/src/violation_detector/config.py +++ b/src/violation_detector/config.py @@ -57,8 +57,10 @@ file = recheck = 无违规,违规不明,检测异常 # 运行模式:cascade=豆包初筛+DeepSeek复检(默认)/ doubao=仅豆包 / deepseek=仅DeepSeek +# 无违规两票复核:DeepSeek 判为无违规的图片是否再追加两票判定(yes/no,默认 yes) [run] mode = cascade +verify_clean = yes """ # 环境变量优先于配置文件 @@ -99,6 +101,7 @@ class AppConfig: recheck_categories: list = field(default_factory=lambda: ["无违规", "违规不明", "检测异常"]) # 运行 mode: str = "cascade" + verify_clean: bool = True retries: int = 3 @property @@ -154,6 +157,14 @@ def load_config(path: str | None = None) -> AppConfig: return conv(parser.get(section, key).strip()) return None + def get_bool(section, key): + if not parser.has_option(section, key): + return None + try: + return parser.getboolean(section, key) + except ValueError: + return None + cfg.ark_api_key = get("ark", "api_key") or cfg.ark_api_key cfg.ark_model = get("ark", "model_id") or cfg.ark_model cfg.ark_base_url = get("ark", "base_url") or cfg.ark_base_url @@ -168,6 +179,8 @@ def load_config(path: str | None = None) -> AppConfig: if raw: cfg.recheck_categories = [s.strip() for s in raw.split(",") if s.strip()] cfg.mode = normalize_mode(get("run", "mode") or cfg.mode) + if get_bool("run", "verify_clean") is not None: + cfg.verify_clean = get_bool("run", "verify_clean") for attr, env in ENV_OVERRIDES.items(): val = os.environ.get(env) @@ -188,7 +201,7 @@ def save_config(cfg: AppConfig, path: str | None = None): "max_tokens": str(cfg.max_tokens)} parser["prompt"] = {"file": cfg.prompt_file or ""} parser["cascade"] = {"recheck": ",".join(cfg.recheck_categories)} - parser["run"] = {"mode": cfg.mode} + parser["run"] = {"mode": cfg.mode, "verify_clean": "yes" if cfg.verify_clean else "no"} with ini.open("w", encoding="utf-8") as f: parser.write(f) diff --git a/src/violation_detector/gui.py b/src/violation_detector/gui.py index 7707732..965af2a 100644 --- a/src/violation_detector/gui.py +++ b/src/violation_detector/gui.py @@ -257,7 +257,7 @@ class App: try: rows, summary = loop.run_until_complete(run_detection( folder, cfg, progress=progress, - stop=self.stop_event.is_set, mode=mode, verify=True)) + stop=self.stop_event.is_set, mode=mode, verify=cfg.verify_clean)) logger.info("正在整理输出(分类归档 + 生成报表)…") ts_dir = organize_output(rows, folder, out_dir) xlsx = build_report(rows, str(ts_dir), diff --git a/tests/test_config.py b/tests/test_config.py index 728f8cc..d664c28 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -63,6 +63,20 @@ def test_mode_from_ini(tmp_path): assert load_config(str(ini)).mode == "cascade" +def test_verify_clean_from_ini(tmp_path): + ini = tmp_path / "config.ini" + ini.write_text("[run]\nverify_clean = no\n", encoding="utf-8") + assert load_config(str(ini)).verify_clean is False + ini.write_text("[run]\nverify_clean = yes\n", encoding="utf-8") + assert load_config(str(ini)).verify_clean is True + # 非法值保持默认开启 + ini.write_text("[run]\nverify_clean = 也许\n", encoding="utf-8") + assert load_config(str(ini)).verify_clean is True + # 未配置保持默认开启 + ini.write_text("[ark]\n", encoding="utf-8") + assert load_config(str(ini)).verify_clean is True + + def test_save_and_load_roundtrip(tmp_path): ini = tmp_path / "config.ini" cfg = AppConfig(ark_api_key="ark-key", ark_model="ep-xyz",