跳到正文

配置说明

配置优先级

Markitai 使用以下优先级顺序(从高到低):

  1. 命令行参数
  2. 环境变量
  3. 配置文件
  4. 默认值

配置文件

Markitai 按以下顺序查找配置文件:

  1. --config 参数指定的路径
  2. MARKITAI_CONFIG 环境变量
  3. ./markitai.json(当前目录)
  4. ~/.markitai/config.json(用户主目录)

初始化配置

bash
# 交互式配置向导(推荐)
markitai init

# 快速模式(生成默认配置)
markitai init --yes

# 在指定位置创建全局配置
markitai init --local  # 创建 ./markitai.json

查看配置

bash
# 列出所有设置
markitai config list
markitai config list --format json    # JSON(默认)
markitai config list --format table   # Rich 表格视图
markitai config list --format yaml    # YAML(需要 pyyaml:uv add pyyaml)
markitai config list --show-secrets   # 显示原始秘密值

# 获取特定值
markitai config get llm.enabled

# 设置值
markitai config set llm.enabled true

# 交互式编辑器(引导式菜单)
markitai config edit

# 验证配置
markitai config validate
markitai config validate ./markitai.json    # 验证指定文件

config list 默认会递归遮罩秘密值,包括嵌套的 API 密钥、Token、Cookie、认证信息及所有自定义 HTTP Header 值。自定义 api_base 只显示 origin。--show-secrets 仅供本机检查,不要把它的完整输出贴到 issue、聊天、CI 日志或其他共享渠道。

完整配置示例

json
{
  "llm": {
    "enabled": false,
    "model_list": [
      {
        "model_name": "default",
        "litellm_params": {
          "model": "gemini/gemini-flash-lite-latest",
          "api_key": "env:GEMINI_API_KEY"
        }
      }
    ],
    "router_settings": {
      "routing_strategy": "simple-shuffle",
      "num_retries": 2,
      "timeout": 120
    },
    "concurrency": 10,
    "max_requests_per_document": 50,
    "max_cost_per_document_usd": 0,
    "max_vision_pages_per_document": 0,
    "pure": false,
    "keep_base": false
  },
  "image": {
    "alt_enabled": false,
    "desc_enabled": false,
    "compress": true,
    "quality": 75,
    "format": "jpeg",
    "max_width": 1920,
    "max_height": 99999,
    "filter": {
      "min_width": 50,
      "min_height": 50,
      "min_area": 5000,
      "deduplicate": true
    },
    "stdout_persist": true,
    "stdout_persist_dir": "~/.markitai/assets",
    "stdout_fetch_external": false
  },
  "ocr": {
    "enabled": false,
    "lang": "en",
    "per_page_routing": true
  },
  "office": {
    "macos_fallback": true
  },
  "screenshot": {
    "enabled": false,
    "screenshot_only": false,
    "viewport_width": 1920,
    "viewport_height": 1080,
    "quality": 75,
    "max_height": 10000
  },
  "cache": {
    "enabled": true,
    "no_cache": false,
    "no_cache_patterns": [],
    "max_size_bytes": 536870912,
    "global_dir": "~/.markitai"
  },
  "batch": {
    "concurrency": 10,
    "url_concurrency": 5,
    "scan_max_depth": 5,
    "scan_max_files": 10000,
    "state_flush_interval_seconds": 10,
    "heavy_task_limit": 0
  },
  "fetch": {
    "strategy": "auto",
    "remote_consent": "always",
    "defuddle": {
      "timeout": 30,
      "rpm": 20
    },
    "playwright": {
      "timeout": 30000,
      "wait_for": "domcontentloaded",
      "extra_wait_ms": 3000,
      "wait_for_selector": null,
      "cookies": null,
      "reject_resource_patterns": null,
      "extra_http_headers": null,
      "user_agent": null,
      "http_credentials": null,
      "session_mode": "isolated",
      "session_ttl_seconds": 600
    },
    "jina": {
      "api_key": null,
      "timeout": 30,
      "rpm": 20,
      "no_cache": false,
      "target_selector": null,
      "wait_for_selector": null
    },
    "cloudflare": {
      "api_token": null,
      "account_id": null,
      "timeout": 30000,
      "wait_until": "networkidle0",
      "cache_ttl": 0,
      "reject_resource_patterns": null,
      "convert_enabled": false,
      "user_agent": null,
      "cookies": null,
      "wait_for_selector": null,
      "http_credentials": null
    },
    "policy": {
      "enabled": true,
      "max_strategy_hops": 5,
      "strategy_priority": null,
      "local_only_patterns": [],
      "inherit_no_proxy": true
    },
    "domain_profiles": {},
    "fallback_patterns": ["x.com", "twitter.com", "instagram.com", "facebook.com", "linkedin.com", "threads.net"]
  },
  "output": {
    "dir": null,
    "on_conflict": "rename",
    "allow_symlinks": false,
    "report": null
  },
  "log": {
    "level": "INFO",
    "format": "text",
    "dir": null,
    "rotation": "10 MB",
    "retention": "7 days"
  },
  "security": {
    "pdf_sanitize": "warn"
  },
  "history": {
    "record": false
  },
  "prompts": {
    "dir": "~/.markitai/prompts"
  }
}

TIP

使用 env:VAR_NAME 语法在配置文件中引用环境变量。对于 JINA_API_KEYCLOUDFLARE_API_TOKENCLOUDFLARE_ACCOUNT_ID,也可以直接设置环境变量(或写入 .env),无需在配置文件中声明,markitai 会自动读取。

环境变量

API 密钥

变量说明
OPENAI_API_KEYOpenAI API 密钥
ANTHROPIC_API_KEYAnthropic (Claude) API 密钥
GEMINI_API_KEYGoogle Gemini API 密钥
DEEPSEEK_API_KEYDeepSeek API 密钥
OPENROUTER_API_KEYOpenRouter API 密钥
JINA_API_KEYJina Reader API 密钥
CLOUDFLARE_API_TOKENCloudflare API Token(Browser Rendering / Workers AI)
CLOUDFLARE_ACCOUNT_IDCloudflare Account ID

Markitai 设置

变量说明
MARKITAI_CONFIG配置文件路径
MARKITAI_LOG_DIR日志文件目录
MARKITAI_LOG_FORMAT日志格式覆盖(textjson
MARKITAI_STATIC_HTTP静态 HTTP 后端:httpx(默认)或 curl_cffi(TLS 指纹伪装)
MARKITAI_LANGCLI 语言覆盖(enzh
MARKITAI_PURE启用 pure 模式(1trueyes
MARKITAI_RECORD_HISTORY将 CLI 运行记录到 markitai serve 历史(1trueyeson;已设置但为假值表示显式关闭)。可被 --record-history / --no-record-history 覆盖;会覆盖配置项 history.record
MARKITAI_NO_REMOTE_FETCH硬性禁用远程提取,包括显式远程 -s 策略(1trueyes
MODELmodel_list 配置时的单模型覆盖

.env 文件加载

Markitai 按以下顺序自动加载 .env 文件(先加载的值优先):

  1. ./.env(当前工作目录,项目级)
  2. ~/.markitai/.env(用户主目录,全局兜底)

项目级 .env 优先级更高,允许按项目覆盖全局设置。

LLM 配置

支持的提供商

任何 LiteLLM 提供商均可使用——OpenAI、Anthropic、Google、DeepSeek、OpenRouter、Ollama(本地)等。基于订阅的本地提供商通过各自的 CLI 认证,无需 API key:

提供商前缀认证方式额外依赖
Claude Agentclaude-agent/Claude Code CLI 登录markitai[claude-agent]
GitHub Copilotcopilot/Copilot CLI 登录markitai[copilot]
ChatGPTchatgpt/首次使用时 OAuth 设备码授权(无需 CLI)

CLI 安装:curl -fsSL https://claude.ai/install.sh | bash(Claude Code;Windows:irm https://claude.ai/install.ps1 | iex),curl -fsSL https://gh.io/copilot-install | bash(Copilot;Windows:winget install GitHub.Copilot)。

Gemini 接入方式

Gemini:使用直连 API 密钥(gemini/,见下方“模型命名”)或通过 OpenRouter 接入(openrouter/google/...)。

模型命名

使用 LiteLLM 模型命名规范:

provider/model-name

示例:

  • openai/gpt-5.6
  • anthropic/claude-sonnet-4-6
  • gemini/gemini-flash-lite-latest
  • deepseek/deepseek-v4-flash
  • ollama/llama3.2
  • claude-agent/sonnet(本地,需要 Claude Code CLI)
  • copilot/gpt-5.6(本地,需要 Copilot CLI)
  • chatgpt/gpt-5.6(本地,需要 ChatGPT 订阅)

markitai 自动选用的默认模型

markitai init、设置向导和凭据自动探测都会选各家的便宜快速档;能用厂商维护的别名就用别名,这样厂商发新版不会让配置失效。受限预览版模型不会被自动选中。

提供商默认模型
Claude Code CLIclaude-agent/sonnet
GitHub Copilotcopilot/claude-haiku-4.5
ChatGPTchatgpt/gpt-5.6
Anthropicanthropic/claude-haiku-4-5
OpenAIopenai/gpt-5.6-luna
Geminigemini/gemini-flash-lite-latest
DeepSeekdeepseek/deepseek-v4-flash
OpenRouteropenrouter/google/gemini-3.1-flash-lite

配置 model_list 即可覆盖。

Claude Agent SDK 支持的模型:

  • 别名(推荐):sonnetopushaikuinherit
  • 完整模型字符串:claude-sonnet-4-6claude-opus-4-6claude-opus-4-5-20251101

GitHub Copilot SDK 支持的模型:

  • 支持 Copilot 订阅可用的所有模型(o1/o3 推理模型除外)
  • 例如:gpt-5.6claude-sonnet-4.6gemini-3.1-pro-preview
  • 可用性取决于您的 Copilot 订阅计划

ChatGPT 支持的模型:

  • gpt-5.6gpt-5.6-codexcodex-mini

已下线模型

以下模型已被厂商下线,不再响应请求:

  • gpt-4ogpt-4.1gpt-4.1-minio4-minigpt-5gpt-5.1gpt-5.2

配置了也只会在启动时告警,markitai 不会替你改写模型。告警里给出的是你所在提供商的默认模型(见 markitai 自动选用的默认模型),下线日期只在 litellm 收录了的情况下才显示。

本地提供商支持 Vision

本地提供商(claude-agent/copilot/chatgpt/)通过文件附件支持图片分析(--alt--desc)。请确保使用支持 vision 的模型(如 copilot/gpt-5.6chatgpt/gpt-5.6)。

本地提供商故障排除

常见错误和解决方案:

错误解决方案
"SDK 未安装"uv add markitai[copilot]uv add markitai[claude-agent]
"CLI 未找到"安装并认证 CLI 工具(Copilot CLIClaude Code
"未认证"运行 copilot auth loginclaude auth login。也可:为 Copilot 设置 COPILOT_GITHUB_TOKEN/GH_TOKEN/GITHUB_TOKEN,为 Claude 设置 CLAUDE_CODE_USE_BEDROCK=1/CLAUDE_CODE_USE_VERTEX=1/CLAUDE_CODE_USE_FOUNDRY=1。ChatGPT 首次使用时自动触发 OAuth。
"速率限制"等待后重试,或检查订阅额度
"请求超时"超时是自适应的;处理非常大的文档可能需要更长时间

使用 markitai doctor 检查认证状态并获取解决方案提示。

自定义 API 端点

使用 api_base 覆盖提供商的默认 API 端点。该值直接传递给 LiteLLM,适用于任何 LiteLLM 支持的提供商(OpenAI、Anthropic、Gemini、Azure 等)。支持与 api_key 相同的 env:变量名 语法:

json
{
  "llm": {
    "model_list": [
      {
        "model_name": "default",
        "litellm_params": {
          "model": "openai/your-model-name",
          "api_key": "env:YOUR_API_KEY",
          "api_base": "https://your-api-endpoint.com/v1"
        }
      }
    ]
  }
}

示例:

json
// 本地 Ollama
{
  "model": "ollama/llama3.2",
  "api_base": "http://localhost:11434"
}

// Azure OpenAI —— "azure/<...>" 填你的 Azure 部署名(你在 Azure Portal 自定义的别名),不是模型 ID
{
  "model": "azure/your-deployment-name",
  "api_key": "env:AZURE_API_KEY",
  "api_base": "https://your-resource.openai.azure.com",
  "api_version": "2025-02-01-preview"
}

// DeepSeek
{
  "model": "deepseek/deepseek-v4-flash",
  "api_key": "env:DEEPSEEK_API_KEY",
  "api_base": "https://api.deepseek.com/v1"
}

// 任何 OpenAI 兼容的提供商
{
  "model": "openai/custom-model",
  "api_key": "env:CUSTOM_API_KEY",
  "api_base": "https://your-proxy-or-provider.com/v1"
}

// 引用环境变量
{
  "model": "anthropic/claude-sonnet-4-6",
  "api_key": "env:ANTHROPIC_API_KEY",
  "api_base": "env:ANTHROPIC_BASE_URL"
}

TIP

常见用例包括自托管推理服务器(vLLM、Ollama、LocalAI)、区域 API 代理和第三方 API 网关。

本地提供商与 api_base

api_base 配置字段不适用于本地提供商(claude-agent/copilot/chatgpt/)。这些提供商作为 CLI 子进程运行或使用 OAuth,内部管理 API 端点:

  • Claude Agent: 设置 ANTHROPIC_BASE_URL 覆盖 API 端点。如果同时设置了 ANTHROPIC_API_KEY,CLI 将使用它进行直接 API 访问而非订阅认证。其他路由选项:CLAUDE_CODE_USE_BEDROCK=1CLAUDE_CODE_USE_VERTEX=1CLAUDE_CODE_USE_FOUNDRY=1
  • GitHub Copilot: 端点由 Copilot CLI 内部管理,不可覆盖。基于令牌的认证请设置 COPILOT_GITHUB_TOKENGH_TOKENGITHUB_TOKEN
  • ChatGPT: 使用 OpenAI Responses API 端点,通过 LiteLLM 内置 OAuth Device Code Flow 认证。

Vision 模型

对于图片分析(--alt--desc),Markitai 自动路由到支持视觉的模型。视觉能力默认自动检测自 litellm,大多数模型无需手动配置。

如需显式覆盖自动检测,设置 supports_vision

json
{
  "llm": {
    "model_list": [
      {
        "model_name": "default",
        "litellm_params": {
          "model": "gemini/gemini-flash-lite-latest",
          "api_key": "env:GEMINI_API_KEY"
        },
        "model_info": {
          "supports_vision": true  // 可选:省略时自动检测
        }
      }
    ]
  }
}

模型 Token 上限

litellm_paramsmodel_info 都支持可选的 token 上限覆盖:

json
{
  "model_name": "default",
  "litellm_params": {
    "model": "gemini/gemini-flash-lite-latest",
    "api_key": "env:GEMINI_API_KEY",
    "max_tokens": 8192
  },
  "model_info": {
    "max_tokens": 8192,
    "max_input_tokens": 1000000
  }
}
字段默认值说明
litellm_params.max_tokensnull覆盖该模型每次调用请求的最大输出 token 数
model_info.max_tokensnull最大输出 token 元数据;省略时从 litellm 自动检测
model_info.max_input_tokensnull最大输入/上下文 token 元数据;省略时从 litellm 自动检测

路由设置

配置 Markitai 如何在多个模型间分发请求:

json
{
  "llm": {
    "router_settings": {
      "routing_strategy": "simple-shuffle",
      "num_retries": 2,
      "timeout": 120,
      "fallbacks": []
    },
    "concurrency": 10,
    "max_requests_per_document": 50,
    "max_cost_per_document_usd": 0,
    "max_vision_pages_per_document": 0
  }
}
设置选项默认值说明
routing_strategysimple-shuffle, least-busy, usage-based-routing, latency-based-routingsimple-shuffle标准模型的选择策略;本地 provider(claude-agent/copilot/ 等)始终按权重随机
num_retries≥02每个请求的传输层重试次数,由 markitai 自己的重试循环执行(LiteLLM 内部重试保持关闭)
timeout120请求超时时间(自适应计算的基础值)
fallbackslist[]LiteLLM 模型组回退,如 [{"default": ["backup"]}]。请求从 default 组进入,其他组名的模型只经回退获得流量(仅标准模型)。留空 = 全部模型合并进 default
concurrency≥110最大并发 LLM 请求数
max_requests_per_document≥050断路器:单文档 LLM 请求数上限(重试全部计入)。触发后跳过该文档剩余增强,保留未增强产物。超大文档请调高;0 关闭
max_cost_per_document_usd≥00断路器:单文档花费上限(美元)。每次拿到回答后计费——调用前无法预知价格——所以它约束的是该文档后续还能花多少,而非跨过阈值的那一次。触发后跳过剩余增强,保留未增强产物。0 关闭
max_vision_pages_per_document≥00断路器:单文档发给视觉模型的页图数上限。发送前检查,超限的文档一分钱不花,改为不带视觉增强地转换。同时限制纯截图 URL 转换读取的截图分块数(只读前几块)。0 关闭

模型权重

model_list 中每个模型的 litellm_params 支持 weight 参数来控制流量分配:

json
{
  "model_name": "default",
  "litellm_params": {
    "model": "gemini/gemini-flash-lite-latest",
    "api_key": "env:GEMINI_API_KEY",
    "weight": 10
  }
}
行为
weight: 0禁用:模型完全排除在路由之外
weight: 1(默认)正常优先级
weight: 10被选中的概率是 weight=1 模型的 10 倍

设置 weight: 0 可临时禁用某个模型而无需删除其配置。至少需要一个模型的 weight > 0,这一校验是在 LLM 路由器真正启动时(首次使用 LLM 时)才会执行,而不是在 markitai config validate 阶段,所以全部模型 weight 都为 0 的配置能通过校验,但会在首次实际使用时失败。

自适应超时

本地 provider(claude-agent/copilot/chatgpt/)使用基于请求复杂度的自适应超时计算

  • 基础超时:最小 60 秒,最大 600 秒
  • 考虑因素:提示词长度、图片存在/数量、预期输出 token 数
  • 计算公式:
    1. timeout = 60 + (提示词字符数 / 500)
    2. 如有预期输出 token 数:加 tokens / 4
    3. 如有图片:timeout *= 1.5(这一步也会连带放大上面的输出 token 项),多张图片时额外加 (图片数 - 1) * 10秒
    4. 限制在 [60, 600] 秒范围内

这可以防止大文档处理超时,同时保持短请求的响应速度。

提示缓存(Claude Agent)

Claude Agent provider 对长度达到 4096 字符(约 4KB)及以上的系统提示词自动启用提示缓存。这通过缓存常用的系统提示词前缀来降低 API 成本。

TIP

提示缓存是透明的,无需配置。使用 markitai cache stats --verbose 查看缓存统计。

图片配置

控制图片处理和压缩:

json
{
  "image": {
    "alt_enabled": false,
    "desc_enabled": false,
    "compress": true,
    "quality": 75,
    "format": "jpeg",
    "max_width": 1920,
    "max_height": 99999,
    "filter": {
      "min_width": 50,
      "min_height": 50,
      "min_area": 5000,
      "deduplicate": true
    }
  }
}
设置默认值说明
alt_enabledfalse通过 LLM 生成 alt 文本
desc_enabledfalse生成图片描述文件
compresstrue压缩图片
quality75JPEG/WebP 质量 (1-100)
formatjpeg输出格式:jpeg, png, webp
max_width1920最大宽度(像素)
max_height99999最大高度(像素,实际无限制)
filter.min_width50跳过宽度小于此值的图片
filter.min_height50跳过高度小于此值的图片
filter.min_area5000跳过面积小于此值的图片
filter.deduplicatetrue去除重复图片
stdout_persisttrue将管道输出图片保存到持久化资产存储
stdout_persist_dir~/.markitai/assets持久化图片存储目录
stdout_fetch_externalfalse在 stdout 模式下下载外部图片 URL

截图配置

为文档和 URL 启用截图捕获:

json
{
  "screenshot": {
    "enabled": false,
    "screenshot_only": false,
    "viewport_width": 1920,
    "viewport_height": 1080,
    "quality": 75,
    "max_height": 10000,
    "tile_height": 2000
  }
}

启用后(--screenshot--preset rich):

  • PDF/PPTX: 将每个页面/幻灯片渲染为 JPEG 图片
  • URL: 使用 Playwright 捕获全页面截图
设置默认值说明
enabledfalse启用截图捕获
screenshot_onlyfalse仅捕获截图,跳过内容提取(对应 --screenshot-only CLI 标志)
viewport_width1920URL 截图的浏览器视口宽度
viewport_height1080URL 截图的浏览器视口高度
quality75JPEG 压缩质量 (1-100)
max_height10000旧版单文件高度上限;当 tile_height 为 0 时使用
tile_height2000更高的 URL 截图会按此高度切成纵向 tile(保持全宽),每块均为 VLM 可读,而不是被整体缩小成一张读不清的图

截图保存在输出目录的 .markitai/screenshots/ 子目录中。

TIP

对于 URL,启用 --screenshot 会在需要时自动将抓取策略升级为 playwright,确保页面完全渲染后再捕获。

预设

Markitai 包含三个内置预设(richstandardminimal)。您还可以在配置文件中定义自定义预设

json
{
  "presets": {
    "my-preset": {
      "llm": true,
      "ocr": false,
      "alt": true,
      "desc": false,
      "screenshot": true
    }
  }
}
字段类型默认值说明
llmbooleanfalse启用 LLM 增强
ocrbooleanfalse启用扫描文档 OCR
altbooleanfalse生成图片 alt 文本
descbooleanfalse生成图片描述
screenshotbooleanfalse启用截图捕获

通过 --preset CLI 标志使用自定义预设:

bash
markitai document.pdf --preset my-preset

OCR 配置

配置扫描文档的光学字符识别。Markitai 使用 RapidOCR(ONNX Runtime + OpenCV)进行 OCR 处理。

json
{
  "ocr": {
    "enabled": false,
    "lang": "en",
    "per_page_routing": true
  }
}
设置默认值说明
enabledfalse为 PDF 及独立图片启用 OCR
langenRapidOCR 语言代码
per_page_routingtrue使用 --ocr 时,未被判定为扫描/乱码的页面保留原生文本层,仅对其余页面执行 OCR;关闭后对每一页都执行 OCR

支持的语言代码:

  • en - 英语
  • zh / ch - 中文(简体)
  • ja / japan - 日语
  • ko / korean - 韩语
  • ar / arabic - 阿拉伯语
  • th - 泰语
  • latin - 拉丁语系

TIP

RapidOCR 已作为依赖包含,开箱即用,无需额外安装。

Office 配置

控制未安装 LibreOffice 时 macOS 上的 MS Office 备选方案。

json
{
  "office": {
    "macos_fallback": true
  }
}
设置默认值说明
macos_fallbacktruemacOS 上未安装 LibreOffice 时,通过 AppleScript 驱动已装的 Microsoft PowerPoint 完成 PPTX 幻灯片渲染

TIP

首次转换会触发每个应用一次性的 macOS 自动化授权弹窗。无头环境(SSH、CI)无法响应弹窗,建议关闭此备选。

批处理配置

控制并行处理:

json
{
  "batch": {
    "concurrency": 10,
    "url_concurrency": 5,
    "scan_max_depth": 5,
    "scan_max_files": 10000,
    "state_flush_interval_seconds": 10,
    "heavy_task_limit": 0
  }
}
设置默认值说明
concurrency10最大并发文件转换数
url_concurrency5最大并发 URL 抓取数(与文件分离)
scan_max_depth5最大目录扫描深度
scan_max_files10000单次运行最大处理文件数
state_flush_interval_seconds10批处理状态持久化到磁盘的间隔(秒)
heavy_task_limit0CPU 密集任务限制(0 = 根据内存自动检测)

TIP

URL 抓取使用独立的并发池,因为 URL 可能有较高延迟(如浏览器渲染的页面)。这可以防止慢速 URL 阻塞本地文件处理。

URL 抓取配置

配置 URL 的抓取方式:

json
{
  "fetch": {
    "strategy": "auto",
    "remote_consent": "always",
    "playwright": {
      "timeout": 30000,
      "wait_for": "domcontentloaded",
      "extra_wait_ms": 3000
    },
    "jina": {
      "api_key": "env:JINA_API_KEY",
      "timeout": 30,
      "rpm": 20,
      "no_cache": false,
      "target_selector": null,
      "wait_for_selector": null
    },
    "cloudflare": {
      "api_token": "env:CLOUDFLARE_API_TOKEN",
      "account_id": "env:CLOUDFLARE_ACCOUNT_ID"
    },
    "fallback_patterns": ["twitter.com", "x.com", "instagram.com", "facebook.com", "linkedin.com", "threads.net"]
  }
}

抓取策略

策略说明
auto自动检测:本地优先顺序(static → playwright → defuddle → jina → cloudflare);已知 SPA/重 JS 域名则改用 playwright → defuddle → jina → cloudflare → static。详见 Fetch Policy 指南
static使用静态 HTTP 抓取和内置 webextract(快速,无 JS)
defuddle使用 Defuddle API 提取干净内容(免费,无需认证)
playwright使用 Playwright 处理 JS 渲染的页面(支持 SPA)
jina使用 Jina Reader API
cloudflare使用 Cloudflare Browser Rendering /content API(取回渲染后的 HTML,本地提取)

远程抓取同意

对于公网 URL,auto 可以在无需确认的情况下回退到远程提取服务。标准域名仍会先尝试本地策略。每个进程第一次准备使用远程服务时,Markitai 会先在 stderr 输出说明,再把当前 URL 交给链路中的下一个服务。说明会完整列出该进程级决定覆盖的服务:defuddle.md、Jina、Cloudflare、FxTwitter 与 Twitter oEmbed。各服务按顺序逐个尝试,不会同时收到该 URL。

对于公网 X/Twitter 状态或文章 URL,本地 DOM 提取失败后,Playwright 可能依次尝试 FxTwitter 与 Twitter oEmbed。这项增强与其他远程服务共用同一个进程级同意决定:在 ask 模式下,它自己也可以弹出那一次共享确认;本次运行中已经做出的决定会被直接沿用;无法询问时则跳过。fetch.remote_consent=neverMARKITAI_NO_REMOTE_FETCH=1 都会禁用它。

私网、本机、内网及自带认证信息的 URL 绝不会使用远程提取,即使显式指定远程策略也不例外。认证信息包括 URL userinfo,以及 query 或 fragment 中的 Token、签名、Credential、密码、API Key 与授权码等敏感参数。在 auto 策略链中,匹配 fetch.policy.local_only_patternsNO_PROXY 的域名也只会留在本机处理(启用 inherit_no_proxy 时)。对于仍属公网的 URL,显式传入非 auto 远程 -s 参数表示有意覆盖这些基于模式的规则。仅在配置文件中设置远程 fetch.strategy 时,仍受 fetch.remote_consent 控制,并使用相同的首次远程揭露。

设置选项默认值说明
fetch.remote_consentask, always, neveralwaysalways:公网 URL 可直接使用远程后备,并在第一次远程尝试前输出说明;ask:每个进程在交互式终端中询问一次,非交互环境跳过全部远程提取服务(含上述 X/Twitter 增强);never:仅使用本地策略

MARKITAI_NO_REMOTE_FETCH=1(或 true/yes)是硬性禁用开关:即使传入 -s defuddle-s jina-s cloudflare,也不会使用远程提取。未设置该环境变量时,显式传入这些 CLI 参数表示本次运行选择该服务,并可为其他公网 URL 覆盖 fetch.remote_consent=neverlocal_only_patterns/NO_PROXY;但私网、本机及自带认证信息 URL 的保护仍然生效。

Playwright 设置

设置默认值说明
timeout30000页面加载超时(毫秒)
wait_fordomcontentloaded等待条件:load, domcontentloaded, networkidle
extra_wait_ms3000JS 渲染额外等待时间
session_modeisolated会话模式:isolated(每个请求新建上下文)、domain_persistent(同域名复用上下文)
session_ttl_seconds600持久化会话的 TTL(秒)
wait_for_selectornull提取前等待的 CSS 选择器
cookiesnull设置 Cookie:[{name, value, domain, path}]
reject_resource_patternsnull屏蔽匹配的资源:["**/*.css"]
extra_http_headersnull额外 HTTP 请求头:{"Accept-Language": "zh-CN"}
user_agentnull自定义 User-Agent 字符串
http_credentialsnullHTTP 认证凭据:{username, password}

Jina 设置

设置默认值说明
api_keynullJina Reader API 密钥(支持 env: 语法)
timeout30请求超时(秒)
rpm20每分钟请求速率限制
no_cachefalse禁用 Jina 服务端缓存
target_selectornull定位特定页面内容的 CSS 选择器
wait_for_selectornull提取前等待的 CSS 选择器

Defuddle 设置

Defuddle 从网页中提取干净的文章内容,移除广告、侧边栏和导航等干扰元素。返回带有丰富 YAML frontmatter(title、author、published、description、word_count)的 Markdown。

设置默认值说明
timeout30请求超时(秒)
rpm20每分钟请求速率限制
json
{
  "fetch": {
    "defuddle": {
      "timeout": 30,
      "rpm": 20
    }
  }
}

TIP

Defuddle 免费且无需 API 密钥或认证。适合文章类网站的默认选择。

Cloudflare 设置

Cloudflare 提供两项能力,各自独立选择:

  1. Browser Rendering-s cloudflare):/content API 取回渲染后的 HTML,再通过与其他策略相同的原生 webextract 流水线本地提取,用于 URL 转 Markdown
  2. Workers AI toMarkdown-b cloudflare):用于文件转 Markdown(PDF、Office、CSV、XML、图片)
json
{
  "fetch": {
    "cloudflare": {
      "api_token": "env:CLOUDFLARE_API_TOKEN",
      "account_id": "env:CLOUDFLARE_ACCOUNT_ID",
      "timeout": 30000,
      "wait_until": "networkidle0",
      "cache_ttl": 0,
      "reject_resource_patterns": null,
      "user_agent": null,
      "cookies": null,
      "wait_for_selector": null,
      "http_credentials": null,
      "convert_enabled": false
    }
  }
}
设置默认值说明
api_tokennullCloudflare API Token(支持 env: 语法)
account_idnullCloudflare Account ID(支持 env: 语法)
timeout30000Browser Rendering 超时(毫秒)
wait_untilnetworkidle0BR 等待事件:load, domcontentloaded, networkidle0
cache_ttl0BR 缓存 TTL(秒,0 = 不缓存)
reject_resource_patternsnull屏蔽匹配正则的资源:["/\\.css$/"]
user_agentnullBrowser Rendering 自定义 User-Agent
cookiesnull导航前设置的 Cookie:[{"name": "k", "value": "v", "url": "..."}]
wait_for_selectornull页面加载后等待的 CSS 选择器(如 "#content"
http_credentialsnullHTTP Basic Auth:{"username": "u", "password": "p"}
convert_enabledfalse启用 Workers AI toMarkdown 文件转换

TIP

Browser Rendering 在 Free 计划上可用。Workers AI toMarkdown 对 PDF/Office/CSV/XML 转换免费;图片转换使用 Neurons 配额。

凭据获取方式:

  1. Account ID:登录 Cloudflare Dashboard,Account ID 显示在 URL 中(dash.cloudflare.com/<account_id>/...),或在任意域名的 Overview 页面右侧边栏中。

  2. API Token:进入 My Profile → API Tokens,点击 Create Token,选择 Custom token 模板,添加以下权限:

    权限访问级别用途
    Account / Cloudflare Workers AIReadtoMarkdown 文件转换
    Account / Browser RenderingEdit/content URL 渲染

    Account Resources 设为目标账户,创建后复制 Token。

  3. 启用 Browser Rendering:在 Cloudflare Dashboard 中进入 Workers & Pages → Browser Rendering,按提示启用(Free 计划可用)。

bash
export CLOUDFLARE_API_TOKEN="your-api-token"
export CLOUDFLARE_ACCOUNT_ID="your-account-id"

限制与注意事项

  • 并发限制:Free 计划允许 2 个并发浏览器实例。Markitai 会自动串行化 CF BR 请求,并在收到 429 限流时指数退避重试,因此高 url_concurrency 值是安全的,但不会加速 CF BR 抓取。
  • 站点兼容性:有严格反爬措施的站点(如 x.com、twitter.com)可能通过 CF BR 返回 400 错误。对这些站点请使用 -s playwright-s jina
  • 文件转换质量:对于有本地 converter 的格式(PDF、DOCX、XLSX 等),CF Workers AI toMarkdown 的输出质量通常低于本地 converter(如格式还原不够精确、无法提取图片等)。使用 -b cloudflare 时如果有更好的本地 converter 可用会输出警告。CF toMarkdown 最适合本地没有 converter 的格式(.numbers.ods.svg 等)。

抓取策略引擎

策略引擎基于域名特征和历史记录智能排序抓取策略。详见 Fetch Policy 指南

json
{
  "fetch": {
    "policy": {
      "enabled": true,
      "max_strategy_hops": 5
    }
  }
}
设置默认值说明
enabledtrue启用智能策略排序
max_strategy_hops5放弃前最多尝试的策略数
strategy_prioritynull自定义全局策略顺序(覆盖默认优先级)
local_only_patterns[]限制为本地策略的域名/IP 模式(NO_PROXY 语法)
inherit_no_proxytrueNO_PROXY 环境变量合并到 local_only_patterns

域名配置

为特定域名配置抓取覆盖:

json
{
  "fetch": {
    "domain_profiles": {
      "x.com": {
        "wait_for_selector": "[data-testid=tweetText]",
        "wait_for": "domcontentloaded",
        "extra_wait_ms": 1200,
        "prefer_strategy": "playwright"
      }
    }
  }
}
设置默认值说明
wait_for_selectornull内容提取前等待的 CSS 选择器
wait_fornull等待条件覆盖:load, domcontentloaded, networkidle(未设置时继承全局 fetch.playwright.wait_for
extra_wait_msnull额外等待时间覆盖(毫秒,未设置时继承全局 fetch.playwright.extra_wait_ms
prefer_strategynull首选策略:static, defuddle, playwright, cloudflare, jina
strategy_prioritynull该域名的自定义策略顺序(覆盖全局和 prefer_strategy
skip_auto_scrollfalse对单内容页面(推文、issue、文档)跳过自动滚动
reject_resource_patternsnull阻止 Playwright 导航中匹配这些 URL 模式的资源(如 ["**/analytics/**"]

Markitai 内置了 x.com/twitter.comgithub.com 的域名配置。为同一域名设置自己的 domain_profiles 条目会整体替换内置配置而非逐字段合并,除非自行重复声明,否则内置调优会丢失。

回退模式

匹配这些模式的网站被视为 SPA/JS 重度依赖站点,在策略顺序中提升浏览器渲染的优先级:

json
{
  "fetch": {
    "fallback_patterns": ["x.com", "twitter.com", "instagram.com", "facebook.com", "linkedin.com", "threads.net"]
  }
}

代理

抓取时优先使用 HTTPS_PROXY / HTTP_PROXY / ALL_PROXYNO_PROXY 为绕过列表)。都未设置时读取系统代理:Windows 的 Internet 设置、macOS 的网络设置,以及 Linux 上当前 GNOME/Unity 或 KDE 桌面的手动 HTTP 代理(含绕过列表)。PAC/WPAD、仅 SOCKS 和带认证的桌面设置不会导入——这类情况和无桌面的机器一样,请设置环境变量。

缓存配置

Markitai 使用全局缓存,存储在 ~/.markitai/cache.db

json
{
  "cache": {
    "enabled": true,
    "no_cache_patterns": [],
    "max_size_bytes": 536870912,
    "global_dir": "~/.markitai"
  }
}
设置默认值说明
enabledtrue启用 LLM 结果缓存
no_cachefalse跳过读取缓存但仍写入(相当于 --no-cache 标志)
no_cache_patterns[]跳过缓存的 glob 模式
max_size_bytes536870912 (512MB)最大缓存大小
global_dir~/.markitai全局缓存目录

缓存命令

bash
# 查看缓存统计
markitai cache stats

# 查看详细统计(条目、按模型分组)
markitai cache stats --verbose

# 指定显示数量
markitai cache stats --verbose --limit 50

# 清除缓存
markitai cache clear
markitai cache clear -y  # 跳过确认

禁用缓存

bash
# 整次运行禁用
markitai document.pdf --no-cache

# 对特定文件/模式禁用
markitai ./docs --no-cache-for "*.pdf"
markitai ./docs --no-cache-for "file1.pdf,reports/**"

输出配置

控制输出文件处理:

json
{
  "output": {
    "on_conflict": "rename"
  }
}
设置选项默认值说明
dir-null输出目录
on_conflictrename, overwrite, skiprename处理已存在文件的方式
allow_symlinks-false允许输出路径中的符号链接
reporttrue, false, nullnull是否写入 JSON 转换报告。null(默认)仅在批量/URL 批量任务时写入;true/false 强制对每次运行开启/关闭
profilerag, obsidian, okf, nullnull面向下游消费者的输出 Profilenull 保持输出不变
wikilinkstrue, falsefalseobsidian profile 下将本地图片引用改写为 wikilink(![[assets/x.png]]

日志配置

配置日志行为:

json
{
  "log": {
    "level": "INFO",
    "format": "text",
    "dir": null,
    "rotation": "10 MB",
    "retention": "7 days"
  }
}
设置默认值说明
levelINFO日志级别:DEBUG, INFO, WARNING, ERROR, CRITICAL
formattext日志格式:text(可读文本)或 json(结构化)
dirnull日志文件目录(未设置时自动检测)
rotation10 MB文件超过此大小时轮转
retention7 days删除早于此时间的日志

安全配置

控制 PDF 隐藏文本的处理。这是 LLM 流水线的一种提示注入攻击面(白底白字、近零尺寸、零透明度或页外内容等本会被静默纳入提取结果的隐藏文本):

json
{
  "security": {
    "pdf_sanitize": "warn"
  }
}
设置选项默认值说明
pdf_sanitizeoff, warn, removewarnwarn 会记录一条汇总提示,标明受影响的页码;remove 还会从输出中剥离匹配到的隐藏文本;off 禁用检测

自定义提示词

自定义不同任务的 LLM 提示词。每个提示词拆分为 system(角色定义)和 user(内容模板)两部分:

json
{
  "prompts": {
    "dir": "~/.markitai/prompts",
    "cleaner_system": null,
    "cleaner_user": null,
    "image_caption_system": null,
    "image_caption_user": null,
    "image_description_system": null,
    "image_description_user": null,
    "image_analysis_system": null,
    "image_analysis_user": null,
    "document_process_system": null,
    "document_process_user": null,
    "document_vision_system": null,
    "document_vision_user": null,
    "url_enhance_system": null,
    "url_enhance_user": null
  }
}

在提示词目录创建自定义提示词文件:

~/.markitai/prompts/
├── cleaner_system.md            # 文档清理角色和规则
├── cleaner_user.md              # 文档清理内容模板
├── image_caption_system.md      # Alt 文本生成角色
├── image_caption_user.md        # Alt 文本内容模板
├── document_process_system.md   # 文档处理角色
└── url_enhance_system.md        # URL 增强角色

指定特定的提示词文件路径:

json
{
  "prompts": {
    "cleaner_system": "/path/to/my-cleaner-system.md",
    "cleaner_user": "/path/to/my-cleaner-user.md"
  }
}

TIP

system/user 拆分可以防止 LLM 意外地将提示词指令包含在其输出中。system 提示词定义角色和规则,而 user 提示词包含实际要处理的内容。

中国大陆用户指南

安装脚本镜像加速

安装脚本会自动检测代理环境变量(HTTPS_PROXY / HTTP_PROXY / ALL_PROXY)。如果未检测到代理,会询问是否启用国内镜像加速,并提供以下镜像源选择:

镜像源PyPInpm推荐地域
清华 TUNA(默认)pypi.tuna.tsinghua.edu.cnregistry.npmmirror.com北方 / 通用
阿里云mirrors.aliyun.comregistry.npmmirror.com东部
腾讯云mirrors.cloud.tencent.commirrors.cloud.tencent.com南方
华为云repo.huaweicloud.commirrors.huaweicloud.com北方

Playwright 浏览器二进制文件统一使用 npmmirror CDN 镜像(cdn.npmmirror.com),这是目前唯一可靠的公共镜像。

你也可以在运行安装脚本前手动设置(以清华 TUNA 为例):

macOS / Linux / WSL (Bash/Zsh):

bash
export UV_INDEX_URL="https://pypi.tuna.tsinghua.edu.cn/simple"
export PLAYWRIGHT_DOWNLOAD_HOST="https://cdn.npmmirror.com/binaries/playwright"
export NPM_CONFIG_REGISTRY="https://registry.npmmirror.com"

Windows (PowerShell):

powershell
$env:UV_INDEX_URL = "https://pypi.tuna.tsinghua.edu.cn/simple"
$env:PLAYWRIGHT_DOWNLOAD_HOST = "https://cdn.npmmirror.com/binaries/playwright"
$env:NPM_CONFIG_REGISTRY = "https://registry.npmmirror.com"

LLM API 访问

国内可用的 LLM 提供商及配置方式:

提供商可用性说明
DeepSeek直连可用无需代理,直接使用 deepseek/deepseek-v4-flash
Ollama完全离线本地模型,使用 ollama/llama3.2
API 代理服务通过中转通过 api_base 指向第三方中转服务
OpenAI / Claude / Gemini需代理需代理或 api_base 中转

使用 api_base 指向代理中转的示例配置:

json
{
  "llm": {
    "model_list": [
      {
        "model_name": "default",
        "litellm_params": {
          "model": "openai/gpt-5.6",
          "api_key": "env:OPENAI_API_KEY",
          "api_base": "https://your-api-proxy.com/v1"
        }
      }
    ]
  }
}

代理配置

如已有代理,设置环境变量即可对所有网络请求生效:

bash
export HTTPS_PROXY="http://127.0.0.1:7890"
export HTTP_PROXY="http://127.0.0.1:7890"
powershell
$env:HTTPS_PROXY = "http://127.0.0.1:7890"
$env:HTTP_PROXY = "http://127.0.0.1:7890"

TIP

设置了代理环境变量后,安装脚本会自动跳过镜像加速配置。