# py_toolkit **Repository Path**: qzcl/py_toolkit ## Basic Information - **Project Name**: py_toolkit - **Description**: python小工具 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-05-15 - **Last Updated**: 2026-05-15 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 🔧 PyToolkit — 轻量级 Python 工具集框架 > 面向**非程序员**的开箱即用工具箱,同时也是面向**开发者**的可扩展插件框架。 > 一份代码,CLI / GUI / EXE 三种使用方式,新增工具只需写一个 `.py` 文件。 [![Python](https://img.shields.io/badge/Python-3.8+-blue.svg)](https://www.python.org/) [![Platform](https://img.shields.io/badge/Platform-Windows%20%7C%20Linux%20%7C%20macOS-lightgrey.svg)]() [![License](https://img.shields.io/badge/License-MIT-green.svg)]() --- ## ✨ 项目特性 | 特性 | 说明 | | --- | --- | | 🧩 **插件式架构** | 在 `src/tools/` 下新建子目录并继承 `BaseTool`,框架启动时自动发现并注册,**零配置接入** | | 📝 **YAML 驱动** | `config/config.yaml` 统一管理所有工具默认参数,CLI / GUI 入参可在运行时覆盖 | | 🪄 **Schema 自动生成表单** | 工具通过 `get_param_schema()` 声明参数,GUI 自动渲染文件选择器、下拉框、复选框等控件 | | 💻 **三端统一** | 一份业务逻辑,同时支持 CLI 命令行、Tkinter 图形界面、PyInstaller 打包 EXE | | 🛡 **预览模式** | 重命名 / 替换等高风险操作默认 `dry_run=true`,先预览再执行,避免误操作 | | 🔍 **全局日志** | 单例 `LoggerManager`,可按工具粒度输出,可选写入文件 | | 📦 **一键打包** | 内置 Windows / Linux / macOS 构建脚本,输出**免 Python 环境**的独立可执行文件 | --- ## 🗂 工程结构 ```text py_toolkit/ ├── main.py # 主入口(启动 CLI / GUI) ├── py_toolkit.spec # PyInstaller 打包配置 ├── requirements.txt # Python 依赖清单 ├── README.md ├── .gitignore ├── config/ │ └── config.yaml # YAML 配置文件(核心!所有工具默认参数集中管理) ├── src/ │ ├── __init__.py │ ├── cli.py # CLI 命令行入口(list / run / gui) │ ├── core/ # 核心框架(与具体工具解耦) │ │ ├── config.py # ConfigManager:单例 + 深度合并 + 热更新 │ │ ├── logger.py # 统一日志配置(控制台 + 文件) │ │ ├── base_tool.py # BaseTool 抽象基类(所有工具继承) │ │ └── registry.py # ToolRegistry:基于 pkgutil 自动发现插件 │ ├── gui/ │ │ └── app.py # tkinter 图形界面(Schema → 表单) │ ├── tools/ # 🔌 工具插件目录(自动发现) │ │ ├── file_processor/ │ │ │ └── batch_rename.py # 内置:批量重命名 │ │ ├── text_processor/ │ │ │ └── text_replace.py # 内置:文本查找替换 │ │ └── excel_processor/ │ │ └── excel_merge.py # 内置:Excel 合并 │ └── utils/ # 通用工具函数(预留) ├── scripts/ │ ├── build.bat # Windows 一键打包 │ └── build.sh # Linux / macOS 一键打包 └── data/ ├── input/ # 默认输入目录 ├── output/ # 默认输出目录 └── temp/ # 临时文件目录 ``` --- ## 🚀 快速开始 ### 1. 环境要求 - Python ≥ 3.8 - Windows / Linux / macOS 任一平台 - 依赖见 [`requirements.txt`](requirements.txt):`pyyaml`、`openpyxl`、`pyinstaller` ### 2. 克隆项目 ```bash git clone cd py_toolkit ``` ### 3. 创建并激活虚拟环境(强烈推荐 ✅) > 使用 venv 隔离依赖,避免污染系统 Python 环境。 **Windows(PowerShell)**: ```powershell # 创建虚拟环境(目录名 .venv 已在 .gitignore 中忽略) python -m venv .venv # 激活 .\.venv\Scripts\Activate.ps1 # 若提示「无法加载...执行策略」,请先一次性放开当前用户: # Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser # 验证:终端提示符前应出现 (.venv) python -V ``` **Windows(cmd)**: ```bat python -m venv .venv .venv\Scripts\activate.bat ``` **Linux / macOS**: ```bash python3 -m venv .venv source .venv/bin/activate ``` **退出虚拟环境**: ```bash deactivate ``` ### 4. 安装依赖 ```bash # 升级 pip(可选) python -m pip install --upgrade pip # 安装项目依赖 pip install -r requirements.txt # 验证依赖已安装 pip list | findstr -i "pyyaml openpyxl pyinstaller" # Windows pip list | grep -iE "pyyaml|openpyxl|pyinstaller" # Linux/macOS ``` ### 5. 运行 ```bash # 查看所有可用工具(首次运行可验证框架是否正常) python main.py list # 启动图形界面(推荐非程序员使用) python main.py gui # CLI 直接运行某个工具 python main.py run batch_rename -p "{\"input_dir\": \"./data/input\", \"mode\": \"prefix\", \"text\": \"new_\"}" # 启用 DEBUG 日志(排查问题时使用) python main.py -v list ``` --- ## 🛠 使用方式 ### 命令行(CLI) | 命令 | 说明 | | --- | --- | | `python main.py list` | 列出所有自动注册的工具 | | `python main.py run -p ''` | 运行指定工具,参数以 JSON 字符串传入 | | `python main.py gui` | 启动图形界面 | | `python main.py -c run ...` | 指定自定义配置文件 | | `python main.py -v ...` | 启用 DEBUG 详细日志 | > 💡 CLI 中 `-p` 参数为 JSON 字符串,会与 `config.yaml` 中 `tools.` 节深度合并,**CLI 参数优先级更高**。 ### 图形界面(GUI) 启动后界面分为三个区域: ``` ┌──────────────────────────────────────────────────┐ │ 🔧 PyToolkit 工具集 v1.0.0 │ ├──────────────────┬───────────────────────────────┤ │ [搜索工具...] │ 工具描述 │ │ │ ┌─────────────────────────┐ │ │ 📦 批量重命名 │ │ 参数设置(自动生成表单) │ │ │ 📦 文本替换 │ │ • 文件选择器 │ │ │ 📦 Excel 合并 │ │ • 下拉框 / 复选框 │ │ │ │ └─────────────────────────┘ │ │ │ [▶ 运行] │ ├──────────────────┴───────────────────────────────┤ │ 状态栏:就绪 - 已加载 N 个工具 │ └──────────────────────────────────────────────────┘ ``` 特性: - 左侧工具列表支持关键字搜索过滤 - 右侧根据 `get_param_schema()` 自动生成表单(文件 / 目录浏览按钮、下拉、复选、数字微调等) - 运行结果通过弹窗与底部状态栏反馈 --- ## 📦 内置工具一览 ### 🔹 batch_rename — 批量重命名(category: `file`) 按规则批量重命名目录下的文件,支持 6 种模式。 | 参数 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | | `input_dir` * | dir | — | 待处理文件所在目录 | | `pattern` | str | `*` | 文件名匹配模式,如 `*.txt`、`image_*.png` | | `mode` * | choice | `prefix` | `prefix` / `suffix` / `sequence` / `replace` / `lowercase` / `uppercase` | | `text` | str | `""` | 添加 / 替换的目标文本 | | `old_text` | str | `""` | `replace` 模式中要被替换的原文本 | | `start_num` | int | `1` | `sequence` 模式起始编号 | | `num_digits` | int | `3` | 序号位数(如 3 → `001`) | | `dry_run` | bool | `true` | **预览模式**,仅展示结果不实际改名 | ```bash # 示例:把 ./data/input 下所有文件加上 "2026_" 前缀(先预览) python main.py run batch_rename -p "{\"input_dir\":\"./data/input\",\"mode\":\"prefix\",\"text\":\"2026_\"}" ``` ### 🔹 text_replace — 文本查找替换(category: `text`) 在多个文本文件中查找并替换内容,支持正则表达式。 | 参数 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | | `input_dir` * | dir | — | 待处理文件目录 | | `pattern` | str | `*.txt` | 文件名匹配模式 | | `search_text` * | str | — | 要查找的文本(可为正则) | | `replace_text` * | str | — | 替换为的文本 | | `use_regex` | bool | `false` | 是否启用正则模式 | | `encoding` | choice | `utf-8` | `utf-8` / `gbk` / `gb2312` / `ascii` / `latin-1` | | `dry_run` | bool | `true` | 预览模式 | ### 🔹 excel_merge — Excel 合并(category: `excel`) 将一个目录下的多个 `.xlsx` 文件合并为单个文件,支持「按行追加」和「按 Sheet 合并」两种模式。 | 参数 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | | `input_dir` * | dir | — | Excel 文件所在目录 | | `output_file` * | file | — | 合并后输出 `.xlsx` 路径 | | `pattern` | str | `*.xlsx` | 文件匹配模式 | | `mode` * | choice | `row` | `row`(行追加)/ `sheet`(多 Sheet) | | `skip_header` | bool | `true` | `row` 模式下跳过后续文件的表头行 | > *打 `*` 的为必填参数。* --- ## ⚙️ 配置文件 `config/config.yaml` 整个项目的「单一可信源」,结构如下: ```yaml app: name: "PyToolkit" version: "1.0.0" language: "zh_CN" log_level: "INFO" # DEBUG / INFO / WARNING / ERROR log_file: "" # 留空仅输出到控制台,如 "logs/app.log" paths: input_dir: "./data/input" # 默认输入目录 output_dir: "./data/output" # 默认输出目录 temp_dir: "./data/temp" # 临时文件目录 tools: # 各工具默认参数(GUI 中作为初始值填充) batch_rename: mode: "prefix" text: "" pattern: "*" start_num: 1 num_digits: 3 dry_run: true text_replace: pattern: "*.txt" use_regex: false encoding: "utf-8" dry_run: true excel_merge: pattern: "*.xlsx" mode: "row" skip_header: true ``` **参数优先级(高 → 低)**: 1. CLI `-p` 传入的 JSON 参数 / GUI 表单填写值 2. `config.yaml` 中 `tools.` 节 3. 工具 Schema 中的 `default` `ConfigManager` 通过点号路径访问任意嵌套配置,例如 `config.get("app.log_level")`,并支持 `set / save / reload` 实现热更新。 --- ## 🧱 扩展开发:新增一个小工具 ### 📌 工具模板(必需包含的内容——缺一不可) 每个小工具都是一个独立的 `.py` 文件,必须严格遵以下模板: ```python """ [工具名称] - 工具一句话描述 """ from typing import Any, Dict from src.core.base_tool import BaseTool # ✅ 必须导入基类 class YourToolName(BaseTool): # ✅ 必须继承 BaseTool # ===== ✅ 必填类属性(框架推送到 CLI、GUI 的元数据)===== name: str = "your_tool" # 唯一标识(CLI 调用名,英文小写 + 下划线) display_name: str = "你的工具名" # GUI 列表显示名 description: str = "一句话描述该工具做什么" category: str = "file" # 分类:file / text / excel / image / ... version: str = "1.0.0" # 工具版本号 # ===== ✅ 必填方法 1:参数 Schema(GUI 据此自动生成表单)===== def get_param_schema(self) -> Dict[str, Dict[str, Any]]: return { "input_path": { # 参数 key(run() 里以同名接收) "type": "file", # 类型:str/int/float/bool/file/dir/choice "label": "输入文件", # GUI 表单标签 "default": "", # 默认值 "required": True, # 是否必填(validate_params 会校验) "description": "详细说明,显示在 GUI", # 参数用法描述 # "choices": ["a", "b"], # 仅 type=choice 必填 # "min": 0, "max": 100, # 仅 type=int 可选 }, "dry_run": { # ⚠️ 强烈建议高风险操作提供预览开关 "type": "bool", "label": "预览模式", "default": True, "required": False, "description": "仅预览结果,不实际执行", }, } # ===== ✅ 必填方法 2:业务逻辑入口 ===== def run(self, **kwargs) -> Any: # 1)取参(kwargs 已被 validate_params 填过默认值并校验过必填项) input_path = kwargs["input_path"] dry_run = kwargs.get("dry_run", True) # 2)使用 self.logger / self.config(由 BaseTool 提供) self.logger.info(f"处理文件: {input_path}") output_dir = self.config.get("paths.output_dir", "./data/output") # 3)执行你的业务逻辑... result = "处理完成" # 4)返回字符串或字典,CLI/GUI 均会展示给用户 return result ``` **另外 2 个必要动作**: 1. 在对应分类目录中存放文件: ```text src/tools/<分类>/<你的文件名>.py ← 例:src/tools/file_processor/md5_check.py ``` > ⚠️ 不能直接放在 `src/tools/` 根下,框架按子包递归扫描。如果是新分类目录,记得加个空的 `__init__.py`。 2. (可选但推荐)在 `config/config.yaml` 的 `tools:` 节下加默认参数: ```yaml tools: your_tool: dry_run: true ``` **检查清单**(必须全部 ✅): - [ ] 类继承了 `BaseTool` - [ ] 定义了非空的 `name`(他是全局唯一 ID) - [ ] 实现了 `run(self, **kwargs)` - [ ] 实现了 `get_param_schema()`(如需 GUI 表单) - [ ] 文件位于 `src/tools/<分类>/` 子目录 - [ ] 高风险操作提供了 `dry_run` 开关 ### 完整示例:添加「文件 MD5 校验」工具 **1. 创建 `src/tools/file_processor/md5_check.py`**: ```python import hashlib from pathlib import Path from typing import Any, Dict from src.core.base_tool import BaseTool class Md5Check(BaseTool): name = "md5_check" display_name = "文件 MD5 校验" description = "计算指定文件的 MD5 哈希值" category = "file" version = "1.0.0" def get_param_schema(self) -> Dict[str, Dict[str, Any]]: return { "file_path": { "type": "file", "label": "目标文件", "required": True, "description": "需要计算 MD5 的文件", }, } def run(self, **kwargs) -> Any: path = Path(kwargs["file_path"]) md5 = hashlib.md5(path.read_bytes()).hexdigest() self.logger.info(f"MD5({path.name}) = {md5}") return f"MD5: {md5}" ``` **2. (可选)在 `config/config.yaml` 中加默认值。** **3. 重启即可,无需任何手动注册**: ```bash python main.py list # 多了一个 md5_check python main.py run md5_check -p "{\"file_path\":\"a.txt\"}" python main.py gui # 左侧列表自动出现 ``` ### 参数 Schema 支持的类型 | `type` | GUI 控件 | 备注 | | --- | --- | --- | | `str` | 单行 Entry | 默认类型 | | `int` | Spinbox | 可配 `min` / `max` | | `float` | Entry | — | | `bool` | Checkbutton | — | | `file` | Entry + 浏览按钮 | 调用文件选择对话框 | | `dir` | Entry + 浏览按钮 | 调用目录选择对话框 | | `choice` | Combobox | 必须配合 `choices: [...]` | 完整字段:`type` / `label` / `default` / `required` / `description` / `choices` / `min` / `max` --- ## 🐛 调试指南(重点!) 新增小工具后,按以下从轻到重的顺序调试。 ### ⚡ 方式 ①:用 `dry_run` + 日志看输出(最快) 1. 在工具中使用 `self.logger.info(...)` / `self.logger.debug(...)` 输出关键变量。 2. 以 **详细日志 + 预览模式** 运行: ```bash python main.py -v run your_tool -p "{\"input_path\":\"./data/input/test.txt\",\"dry_run\":true}" ``` 3. 查看控制台输出。可为本次调试临时打开文件日志: ```yaml # config/config.yaml app: log_level: "DEBUG" # 从 INFO 改为 DEBUG log_file: "logs/app.log" # 输出到文件便于查阅 ``` ### ⚡ 方式 ②:使用 `breakpoint()` / `pdb` 交互式调试 在你考虑出问题的位置插入 `breakpoint()`(Python 3.7+ 内置): ```python def run(self, **kwargs): input_path = kwargs["input_path"] breakpoint() # 👈 运行到此会进入 pdb ... ``` 运行: ```bash python main.py run your_tool -p "{...}" ``` 进入 pdb 后常用命令: - `n` 下一行 1`s` 进入函数、`c` 继续、`l` 查看上下文 - `p ` 打印变量、`pp ` 美化打印 - `q` 退出 ### ⚡ 方式 ③:VS Code 断点调试(推荐) 在项目根创建 `.vscode/launch.json`(如不存在): ```json { "version": "0.2.0", "configurations": [ { "name": "Debug: list", "type": "debugpy", "request": "launch", "program": "${workspaceFolder}/main.py", "args": ["-v", "list"], "console": "integratedTerminal", "cwd": "${workspaceFolder}", "justMyCode": false }, { "name": "Debug: run your_tool", "type": "debugpy", "request": "launch", "program": "${workspaceFolder}/main.py", "args": [ "-v", "run", "your_tool", "-p", "{\"input_path\":\"./data/input/test.txt\",\"dry_run\":true}" ], "console": "integratedTerminal", "cwd": "${workspaceFolder}", "justMyCode": false }, { "name": "Debug: GUI", "type": "debugpy", "request": "launch", "program": "${workspaceFolder}/main.py", "args": ["gui"], "console": "integratedTerminal", "cwd": "${workspaceFolder}", "justMyCode": false } ] } ``` 使用步骤: 1. 在你的工具 `run()` 代码行号旁点击设断点 ⚫ 2. 选择右上角 「Debug: run your_tool」 配置 3. F5 启动调试,会准确停在断点处 4. 左侧可查看变量 / 调用栈,F10 单步、F11 步入、F5 继续 > ❗ `justMyCode: false` 可以调入 `BaseTool` / `ConfigManager` 等框架代码,定位参数传递问题时十分有用。 ### ⚡ 方式 ④:PyCharm 调试 1. `Run` → `Edit Configurations...` → `+` → `Python` 2. 配置: - **Script path**:`<项目根>/main.py` - **Parameters**:`-v run your_tool -p {"input_path":"./data/input/test.txt","dry_run":true}` - **Working directory**:项目根 - **Python interpreter**:选择 `.venv` 中的 Python 3. 设断点 → 点击 🐞 Debug 图标启动 ### ⚡ 方式 ⑤:给工具加 `__main__` 独立运行入口(最适合快速迭代) 在工具文件底部追加以下代码,可**跳过 CLI 直接运行**单文件: ```python if __name__ == "__main__": # 使能独立运行本文件进行调试 import sys from pathlib import Path sys.path.insert(0, str(Path(__file__).resolve().parents[3])) # 将项目根加入 sys.path from src.core.config import ConfigManager config = ConfigManager("config/config.yaml") tool = YourToolName(config=config) test_params = { "input_path": "./data/input/test.txt", "dry_run": True, } validated = tool.validate_params(test_params) print(tool.run(**validated)) ``` 运行: ```bash python -m src.tools.file_processor.your_tool # 或者在 IDE 中右键该文件 → Run / Debug ``` ### ⚡ 方式 ⑥:编写最小单元测试(适合复杂逻辑) 在项目根创建 `tests/test_your_tool.py`: ```python import unittest from src.tools.file_processor.your_tool import YourToolName from src.core.config import ConfigManager class TestYourTool(unittest.TestCase): def setUp(self): ConfigManager.reset() # 重置单例避免试例间干扰 self.tool = YourToolName(config=ConfigManager()) def test_basic(self): result = self.tool.run(input_path="./data/input/test.txt", dry_run=True) self.assertIn("处理完成", result) if __name__ == "__main__": unittest.main() ``` 运行: ```bash python -m unittest tests.test_your_tool -v ``` ### 🔍 常见调试场景与排查 | 现象 | 可能原因 | 排查方法 | | --- | --- | --- | | `python main.py list` 看不到新工具 | 文件不在子包里 / 未继承 BaseTool / `name` 为空 | 查看启动时控制台是否打印 `加载模块 xxx 失败` | | 报 `缺少必填参数: xxx` | Schema 中 `required=True` 但未传入 | CLI `-p` 补全参数,或在 `config.yaml` 加默认值 | | GUI 表单控件不出现 | `get_param_schema()` 未返回该参数,或 `type` 拼写错 | 检查 Schema;重调报错可看控制台 | | 在 IDE 中运行报 `ModuleNotFoundError: src` | 工作目录不对 | 将运行配置的 `cwd` 设为项目根 | | 依赖报 `ModuleNotFoundError` | 未激活 venv | `which python` / `where python` 检查是否指向 `.venv` | | 中文路径乱码 | 文件未以 UTF-8 保存,或读写未指定 `encoding="utf-8"` | 统一使用 UTF-8 | | 打包后 EXE 找不到新工具 | `py_toolkit.spec` 的 `hiddenimports` 未补充 | 添加对应的模块路径后重新打包 | ### 🎯 推荐调试流水线 ```text 写完代码 ↓ [1] python main.py list → 验证工具被发现 ↓ [2] python main.py -v run your_tool -p "{...,\"dry_run\":true}" → dry_run 验证逻辑 ↓ [3] 有问题?加断点用 VS Code/PyCharm Debug 配置进入调试 ↓ [4] 复杂场景?补充 unittest ↓ [5] 打开 dry_run=false 实际运行 → python main.py gui 验证表单 ``` --- ## 📦 打包为独立 EXE 借助 PyInstaller 打包成**免 Python 环境**的可执行文件。 ### Windows ```bat scripts\build.bat ``` ### Linux / macOS ```bash chmod +x scripts/build.sh ./scripts/build.sh ``` 打包流程(脚本内自动完成): 1. 检测 Python 环境 2. `pip install -r requirements.txt` 3. `pyinstaller py_toolkit.spec --clean --noconfirm` 4. 产物输出至 `dist/PyToolkit(.exe)` ### `py_toolkit.spec` 关键点 - `datas`:将 `config/config.yaml` 一并打包 - `hiddenimports`:显式声明 `src.tools.*` 下的所有插件模块(解决动态导入问题) - `excludes`:剔除 `numpy / pandas / matplotlib` 等大体积无关库,**显著减小产物体积** - `console=True`:保留控制台窗口;如做纯 GUI 发布,改为 `False` - `icon='assets/icon.ico'`:自定义图标(需自行准备) > ⚠️ **新增工具后请同步追加 `hiddenimports`**,否则打包后插件无法被发现。 --- ## 🧠 核心架构图 ```mermaid graph TD A[main.py] --> B[src/cli.py] B --> C[ConfigManager] B --> D[ToolRegistry] B --> E{command?} E -->|list| F[print tools] E -->|run| G[load tool & run] E -->|gui| H[ToolApp] D -->|discover_tools| I[src/tools/**] I --> J[BaseTool 子类] J -->|get_param_schema| H H -->|render form| K[GUI 表单] G --> L[validate_params + run] ``` 执行链路: 1. `main.py` → `cli.main()` 2. `ConfigManager` 单例加载 `config/config.yaml`,与默认配置深度合并 3. `ToolRegistry.discover_tools()` 通过 `pkgutil.iter_modules` 递归扫描 `src.tools.*`,自动注册所有 `BaseTool` 子类 4. 根据子命令分发: - `list` → 输出工具元信息 - `run` → 合并配置 + CLI 参数 → `validate_params` → `run(**validated)` - `gui` → `ToolApp` 渲染左侧列表 + 右侧根据 Schema 动态生成表单 --- ## ❓ 常见问题(FAQ) **Q1:新增工具后 `python main.py list` 看不到?** - 确认类继承自 `BaseTool` 且定义了非空的 `name` 属性。 - 确认文件位于 `src/tools/<分类>/` 子目录中(不能直接放在 `src/tools/` 根下,框架按子包扫描)。 - 检查文件是否存在 import 错误(启动时会打印 `加载模块 xxx 失败`)。 **Q2:打包 EXE 后报错「找不到工具」?** - 在 `py_toolkit.spec` 的 `hiddenimports` 中追加新模块路径,重新打包。 **Q3:YAML 中的中文路径 / 中文参数乱码?** - `ConfigManager` 默认以 UTF-8 读取,请确保 `config.yaml` 也以 UTF-8 保存。 **Q4:如何让某个工具不显示控制台窗口?** - 修改 `py_toolkit.spec` 中 `console=True` 为 `False`,重新打包;适用于纯 GUI 场景。 **Q5:日志输出到文件?** - 修改 `config/config.yaml`: ```yaml app: log_level: "DEBUG" log_file: "logs/app.log" ``` --- ## 📝 开发约定 - 工具类必须继承 `BaseTool` 并实现 `run(**kwargs)` - 必填参数在 Schema 中声明 `"required": True`,由 `validate_params` 统一校验 - 高风险操作(删除、覆盖、改名)建议提供 `dry_run` 预览参数,默认 `True` - 工具内部使用 `self.logger` 输出日志,`self.config` 读取全局配置 - 输出目录使用 `self.ensure_dir(path)` 确保存在 --- ## 📜 License MIT © PyToolkit Contributors