feat: 添加 --advice 参数,支持快速获取执行建议

- 新增 scripts/core/advice_generator.py 建议生成器模块
- 在 config.py 中添加 DEPENDENCIES 依赖配置
- 在 lyxy_document_reader.py 中添加 -a/--advice 参数
- 复用 Reader 实例的 supports 方法检测文件类型
- 支持平台检测,对 macOS x86_64 PDF 返回特殊命令
- 添加单元测试和集成测试
- 更新 SKILL.md,引导优先使用 --advice 参数
- 更新 README.md,添加项目结构说明
- 添加 openspec/specs/cli-advice/spec.md 规范文档
This commit is contained in:
2026-03-09 18:13:00 +08:00
parent 9daff73589
commit aaa1171e60
9 changed files with 757 additions and 103 deletions

View File

@@ -0,0 +1,251 @@
"""建议生成器模块,根据文件类型和平台返回执行建议。"""
import platform
from pathlib import Path
from typing import Dict, Optional, Tuple, List, Type
from config import DEPENDENCIES
from readers import BaseReader
from readers import (
PdfReader,
DocxReader,
XlsxReader,
PptxReader,
HtmlReader,
)
# Reader 类到配置 key 的映射
_READER_KEY_MAP: Dict[Type[BaseReader], str] = {
PdfReader: "pdf",
DocxReader: "docx",
XlsxReader: "xlsx",
PptxReader: "pptx",
HtmlReader: "html",
}
def detect_file_type_light(input_path: str, readers: List[BaseReader]) -> Optional[Type[BaseReader]]:
"""
轻量文件类型检测,复用 Reader 的 supports 方法。
Args:
input_path: 文件路径或 URL
readers: 已实例化的 reader 列表
Returns:
支持该输入的 Reader 类,无法识别返回 None
"""
for reader in readers:
if reader.supports(input_path):
return reader.__class__
return None
def get_platform() -> str:
"""
获取当前平台标识,格式为 {system}-{machine}
Returns:
平台标识,例如 "Darwin-arm64""Linux-x86_64""Windows-AMD64"
"""
system = platform.system()
machine = platform.machine()
return f"{system}-{machine}"
def get_dependencies(reader_cls: Type[BaseReader], platform_id: str) -> Tuple[Optional[str], list]:
"""
获取指定 Reader 类和平台的依赖配置。
Args:
reader_cls: Reader 类
platform_id: 平台标识(如 "Darwin-arm64"
Returns:
(python_version, dependencies) 元组
- python_version: 需要的 python 版本None 表示使用默认
- dependencies: 依赖包列表
"""
key = _READER_KEY_MAP.get(reader_cls)
if not key or key not in DEPENDENCIES:
return None, []
type_config = DEPENDENCIES[key]
# 先尝试匹配特定平台
if platform_id in type_config:
config = type_config[platform_id]
return config.get("python"), config.get("dependencies", [])
# 使用 default 配置
if "default" in type_config:
config = type_config["default"]
return config.get("python"), config.get("dependencies", [])
return None, []
def generate_uv_command(
dependencies: list,
input_path: str,
python_version: Optional[str] = None,
script_path: str = "scripts/lyxy_document_reader.py"
) -> str:
"""
生成 uv run 命令。
Args:
dependencies: 依赖包列表
input_path: 输入文件路径或 URL
python_version: 需要的 python 版本None 表示不指定
script_path: 脚本路径
Returns:
uv run 命令字符串
"""
parts = ["uv run"]
if python_version:
parts.append(f"--python {python_version}")
for dep in dependencies:
# 处理包含空格的依赖(如 unstructured[pdf]),需要加引号
if "[" in dep or " " in dep:
parts.append(f'--with "{dep}"')
else:
parts.append(f"--with {dep}")
parts.append(f"{script_path} {input_path}")
return " ".join(parts)
def generate_python_command(
dependencies: list,
input_path: str,
script_path: str = "scripts/lyxy_document_reader.py"
) -> Tuple[str, str]:
"""
生成 python 命令和 pip 安装命令。
Args:
dependencies: 依赖包列表
input_path: 输入文件路径或 URL
script_path: 脚本路径
Returns:
(python_command, pip_command) 元组
"""
python_cmd = f"python {script_path} {input_path}"
# 构建 pip install 命令,处理带引号的依赖
pip_parts = ["pip install"]
for dep in dependencies:
pip_parts.append(dep)
pip_cmd = " ".join(pip_parts)
return python_cmd, pip_cmd
def format_advice(
file_type: str,
input_path: str,
platform_id: str,
uv_command: str,
python_command: str,
pip_command: str,
has_platform_specific: bool = False
) -> str:
"""
格式化建议输出。
Args:
file_type: 文件类型
input_path: 输入路径
platform_id: 平台标识
uv_command: uv 命令
python_command: python 命令
pip_command: pip 安装命令
has_platform_specific: 是否使用了平台特殊配置
Returns:
格式化后的建议文本
"""
lines = []
# 文件类型和输入路径
lines.append(f"文件类型: {file_type.upper()}")
lines.append(f"输入路径: {input_path}")
# 平台信息(仅当使用了特殊配置时显示)
if has_platform_specific:
lines.append(f"平台: {platform_id}")
lines.append("")
# uv 命令
lines.append("[uv 命令]")
lines.append(uv_command)
lines.append("")
# python 命令
lines.append("[python 命令]")
lines.append(python_command)
lines.append(pip_command)
return "\n".join(lines)
def generate_advice(
input_path: str,
readers: List[BaseReader],
script_path: str = "scripts/lyxy_document_reader.py"
) -> Optional[str]:
"""
生成完整的执行建议。
Args:
input_path: 输入文件路径或 URL
readers: 已实例化的 reader 列表
script_path: 脚本路径
Returns:
格式化的建议文本,无法识别文件类型返回 None
"""
# 检测文件类型,获取 Reader 类
reader_cls = detect_file_type_light(input_path, readers)
if not reader_cls:
return None
# 获取配置 key 和显示名称
key = _READER_KEY_MAP.get(reader_cls, "unknown")
file_type = key
# 获取平台
platform_id = get_platform()
# 获取依赖配置
python_version, dependencies = get_dependencies(reader_cls, platform_id)
# 判断是否使用了平台特殊配置
has_platform_specific = False
if key in DEPENDENCIES:
type_config = DEPENDENCIES[key]
if platform_id in type_config and "default" in type_config:
has_platform_specific = True
# 生成命令
uv_command = generate_uv_command(dependencies, input_path, python_version, script_path)
python_command, pip_command = generate_python_command(dependencies, input_path, script_path)
# 格式化输出
return format_advice(
file_type,
input_path,
platform_id,
uv_command,
python_command,
pip_command,
has_platform_specific
)