只要纠错、尽量保留作者原话时,应走
/v2/write/correct;需要语气/文体时才用 rephrase。这篇用write_modes.py在本地强制互斥规则,并对比两种模式输出。
先看清成品
- dry-run 能打出正确 path/payload。
- mock 上 correct 与 business rephrase 结果不同。
- 3 项 pytest 通过。
合规提醒:DeepL Write API 目前需要 API Pro(以官方说明为准),Free 计划可能不可用。密钥用
Authorization: DeepL-Auth-Key ...,勿提交到公开仓库。Write 是同语种润色,不是翻译;不要用它处理你无权处理的保密文本(注意 Pro 数据保护条款)。
运行说明:无 DeepL Pro 密钥。A/B/C 均用本地 mock 验证;对 api-free 用无效密钥实测 Write 返回 HTTP 403 鉴权失败。2026-10-23(Asia/Shanghai)pytest 通过。正式调用基址一般为 https://api.deepl.com 。
做完之后你会得到什么?
| 成果 | 说明 |
|---|---|
write_modes.py |
模式选择器 |
mock_deepl_write.py |
假服务 |
test_98b.py |
3 项测试 |
需要准备哪些环境?
| 项目 | 说明 |
|---|---|
| Python | 3.10+ |
关键事实
| 事实 | 来源 |
|---|---|
| correct 不接受 writing_style/tone | 官方 Correct text |
| rephrase 上 style 与 tone 不能同时出现;可用 prefer_ 回退 | 官方 Style and tone |
| 请求体总大小限制约 10 KiB | 官方 Write Quickstart |
文件结构
98-deepl-write-B-src/
├── write_modes.py
├── mock_deepl_write.py
└── test_98b.py
第 1 步:模式脚本
#!/usr/bin/env python3
"""Choose DeepL Write endpoint and validate style/tone exclusivity."""
from __future__ import annotations
import argparse
import json
import os
import sys
import urllib.error
import urllib.request
def build_payload(texts, target_lang, mode, style, tone):
if mode not in {"rephrase", "correct"}:
raise ValueError("mode must be rephrase or correct")
if style and tone:
raise ValueError("writing_style and tone are mutually exclusive")
if mode == "correct" and (style or tone):
raise ValueError("correct endpoint rejects writing_style/tone")
payload = {"text": texts, "target_lang": target_lang}
if mode == "rephrase":
if style:
payload["writing_style"] = style
if tone:
payload["tone"] = tone
return payload
def call(base, key, mode, payload):
path = f"/v2/write/{mode}"
req = urllib.request.Request(
f"{base.rstrip('/')}{path}",
data=json.dumps(payload).encode(),
headers={"Authorization": f"DeepL-Auth-Key {key}", "Content-Type": "application/json"},
method="POST",
)
try:
with urllib.request.urlopen(req, timeout=30) as resp:
return json.loads(resp.read().decode())
except urllib.error.HTTPError as e:
raise SystemExit(f"HTTP {e.code}: {e.read().decode(errors='replace')}") from e
def main(argv=None) -> int:
ap = argparse.ArgumentParser(description=__doc__)
ap.add_argument("text")
ap.add_argument("--mode", choices=["rephrase", "correct"], default="correct")
ap.add_argument("--style", default=None)
ap.add_argument("--tone", default=None)
ap.add_argument("--lang", default="en-US")
ap.add_argument("--base", default=os.environ.get("DEEPL_BASE_URL", "https://api.deepl.com"))
ap.add_argument("--key", default=os.environ.get("DEEPL_API_KEY", ""))
ap.add_argument("--dry-run", action="store_true")
args = ap.parse_args(argv)
try:
payload = build_payload([args.text], args.lang, args.mode, args.style, args.tone)
except ValueError as e:
print(f"INVALID: {e}", file=sys.stderr)
return 2
if args.dry_run:
print(json.dumps({"path": f"/v2/write/{args.mode}", "payload": payload}, ensure_ascii=False, indent=2))
return 0
if not args.key:
print("设置 DEEPL_API_KEY", file=sys.stderr)
return 2
r = call(args.base, args.key, args.mode, payload)
print((r.get("improvements") or [{}])[0].get("text"))
return 0
if __name__ == "__main__":
raise SystemExit(main())
第 2 步:测试
import subprocess
import sys
import time
from pathlib import Path
from write_modes import build_payload
HERE = Path(__file__).resolve().parent
def test_mutex():
try:
build_payload(["hi"], "en-US", "rephrase", "business", "friendly")
assert False
except ValueError:
pass
def test_correct_rejects_style():
try:
build_payload(["hi"], "en-US", "correct", "business", None)
assert False
except ValueError:
pass
def test_correct_vs_rephrase():
port = 18981
proc = subprocess.Popen(
[sys.executable, str(HERE / "mock_deepl_write.py"), "--port", str(port)],
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
)
try:
time.sleep(0.3)
env_base = f"http://127.0.0.1:{port}"
c = subprocess.run(
[
sys.executable,
str(HERE / "write_modes.py"),
"I could relly use sum help with edits on thiss text !",
"--mode",
"correct",
"--base",
env_base,
"--key",
"deepl-test-key",
],
capture_output=True,
text=True,
check=True,
)
r = subprocess.run(
[
sys.executable,
str(HERE / "write_modes.py"),
"I could relly use sum help with edits on thiss text !",
"--mode",
"rephrase",
"--style",
"business",
"--base",
env_base,
"--key",
"deepl-test-key",
],
capture_output=True,
text=True,
check=True,
)
assert "really" in c.stdout.lower()
assert c.stdout.strip() != r.stdout.strip()
finally:
proc.terminate()
proc.wait(timeout=5)
第 3 步:运行
INVALID: writing_style and tone are mutually exclusive
{
"path": "/v2/write/correct",
"payload": {
"text": [
"hello"
],
"target_lang": "en-US"
}
}
... [100%]
3 passed in 0.42s
怎么检查结果?
--dry-run时不联网。- correct 模式加 --style 应被本地拒绝。
调试示例
UI「Corrections Only」对应 correct 端点。 若你在网页端选了只纠错,API 集成却调用 rephrase,作者会抱怨「被改腔调」。先在客户端层把模式选死。
常见问题
| 现象 | 原因 | 处理 |
|---|---|---|
| 想要礼貌语气 | 用 rephrase + tone | 不要塞进 correct |
| 检测到英式/美式 | target_lang | en-GB / en-US |
| 400 | 非法枚举 | 对照官方 enum |
模板规则
- 模式与产品语义一致。
- 互斥参数本地先挡。
- 对比样例留档。
项目资源
| 用途 | 地址 |
|---|---|
| Correct text(官方) | https://developers.deepl.com/api-reference/improve-text/correct-text |
| Style and tone(官方) | https://developers.deepl.com/docs/translate/controlling-writing-style-and-tone |
| Improve text(官方) | https://developers.deepl.com/api-reference/improve-text/request-text-improvement |
选对端点,比调温度更重要。