init: 商品图合规检测工具(豆包初筛 + DeepSeek 复检级联)
- src 标准布局:config/providers/pipeline/report + CLI/Tk GUI 双入口 - 级联省钱:豆包全量初筛,仅无违规/违规不明图进 DeepSeek 复检(含两票复核) - 输出:时间戳目录 + 分类文件夹图片归档 + Excel 报表 - 30 个单元测试(tests/,测试图片不入库)
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""CLI 入口:python -m violation_detector <图片文件夹> [选项]"""
|
||||
import argparse
|
||||
import asyncio
|
||||
import sys
|
||||
|
||||
from . import __version__
|
||||
from .config import DEFAULT_CONFIG, load_config, missing_fields
|
||||
from .pipeline import run_detection
|
||||
from .report import build_report, organize_output
|
||||
|
||||
|
||||
def build_parser() -> argparse.ArgumentParser:
|
||||
p = argparse.ArgumentParser(
|
||||
prog="violation-detector",
|
||||
description="服装商品图合规检测:豆包视觉初筛 + DeepSeek 复检级联,输出 Excel 报表")
|
||||
p.add_argument("folder", help="待检测图片文件夹")
|
||||
p.add_argument("-o", "--output", default=None, help="报表输出目录(默认为图片文件夹)")
|
||||
p.add_argument("-c", "--config", default=None, help="配置文件路径(默认 config.ini)")
|
||||
p.add_argument("--prompt", default=None, help="提示词文件路径(覆盖配置)")
|
||||
p.add_argument("--mode", choices=["cascade", "doubao", "deepseek"], default="cascade",
|
||||
help="检测模式:cascade=豆包初筛+DeepSeek复检(默认),doubao=仅豆包,"
|
||||
"deepseek=仅 DeepSeek 全量")
|
||||
p.add_argument("--workers-ark", type=int, default=None, help="豆包并发数")
|
||||
p.add_argument("--workers-ds", type=int, default=None, help="DeepSeek 并发数")
|
||||
p.add_argument("--max-tokens", type=int, default=None,
|
||||
help="DeepSeek max_tokens(默认 5000,实测零失败最小档)")
|
||||
p.add_argument("--no-verify", action="store_true", help="跳过 DeepSeek 无违规两票复核")
|
||||
p.add_argument("--version", action="version", version=f"%(prog)s {__version__}")
|
||||
return p
|
||||
|
||||
|
||||
def main(argv=None) -> int:
|
||||
args = build_parser().parse_args(argv)
|
||||
cfg = load_config(args.config)
|
||||
if args.prompt:
|
||||
cfg.prompt_file = args.prompt
|
||||
if args.workers_ark:
|
||||
cfg.ark_workers = args.workers_ark
|
||||
if args.workers_ds:
|
||||
cfg.deepseek_workers = args.workers_ds
|
||||
if args.max_tokens:
|
||||
cfg.max_tokens = args.max_tokens
|
||||
|
||||
missing = missing_fields(cfg)
|
||||
if missing:
|
||||
print(f"配置不完整,请编辑 {DEFAULT_CONFIG} 填写以下必填项:")
|
||||
for m in missing:
|
||||
print(f" - {m}")
|
||||
return 2
|
||||
|
||||
try:
|
||||
rows, summary = asyncio.run(run_detection(
|
||||
args.folder, cfg, mode=args.mode, verify=not args.no_verify))
|
||||
except FileNotFoundError as e:
|
||||
print(f"错误:{e}")
|
||||
return 1
|
||||
|
||||
out_dir = args.output or args.folder
|
||||
ts_dir = organize_output(rows, args.folder, out_dir)
|
||||
xlsx = build_report(rows, str(ts_dir), {"mode": args.mode, **{
|
||||
k: summary.get(k) for k in ("ds_calls", "ds_peak_cost", "ds_idle_cost")}})
|
||||
print(f"\n分类输出目录:{ts_dir}")
|
||||
print(f"报表已生成:{xlsx}")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user