打开
enable_memory时需传history_data:元素含is_sent(bool)与message。这篇先校验历史格式,再向 mock 发一轮续写。
先看清成品
- 样例历史通过校验;mock 响应含 hist=3。
- 2 项 pytest 通过。
合规提醒:Writesonic API 密钥在控制台激活后仅显示一次副本,勿提交公开仓库。SEO/AI 可见度类功能变化较快,以官网与文档为准;本文不编造价格。生成内容需避免抄袭与虚假宣传,并遵守目标市场广告法。
运行说明:无正式密钥。A/C 用 mock Chatsonic;对官方主机无效密钥实测 HTTP 401。B 为本地 SEO brief 校验。2026-10-23(Asia/Shanghai)pytest 通过。
做完之后你会得到什么?
| 成果 | 说明 |
|---|---|
history_check.py |
校验+调用 |
sample_history.json |
样例 |
mock_writesonic.py |
假服务 |
test_99c.py |
测试 |
需要准备哪些环境?
| 项目 | 说明 |
|---|---|
| Python | 3.10+ |
关键事实
| 事实 | 来源 |
|---|---|
history 项:is_sent + message;用户消息 is_sent=true |
官方 Memory 说明 / 社区客户端约定 |
| enable_memory 与 history_data 一起使用 | 官方 Chatsonic |
| input_text 仍受 1..600 限制 | 官方 |
文件结构
99-writesonic-C-src/
├── history_check.py
├── sample_history.json
├── mock_writesonic.py
└── test_99c.py
第 1 步:历史脚本
#!/usr/bin/env python3
"""Validate Chatsonic history_data format and run a memory-enabled mock turn."""
from __future__ import annotations
import argparse
import json
import os
import sys
import urllib.error
import urllib.request
from pathlib import Path
def validate_history(history: list) -> list[str]:
errs = []
if not isinstance(history, list):
return ["history_data 必须是数组"]
for i, item in enumerate(history):
if not isinstance(item, dict):
errs.append(f"[{i}] 不是对象")
continue
if "is_sent" not in item or "message" not in item:
errs.append(f"[{i}] 需要 is_sent 与 message")
continue
if not isinstance(item["is_sent"], bool):
errs.append(f"[{i}] is_sent 必须是 bool")
if not isinstance(item["message"], str) or not item["message"].strip():
errs.append(f"[{i}] message 必须是非空字符串")
# optional: alternate user/assistant
if history and isinstance(history[0], dict) and history[0].get("is_sent") is not True:
errs.append("首条通常应为用户消息 is_sent=true")
return errs
def chat(base, key, text, history):
payload = {
"input_text": text,
"enable_google_results": False,
"enable_memory": True,
"history_data": history,
}
req = urllib.request.Request(
f"{base.rstrip('/')}/v2/business/content/chatsonic?engine=premium",
data=json.dumps(payload).encode(),
headers={"X-API-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("history_json")
ap.add_argument("--message", default="继续上一个话题,给三个要点")
ap.add_argument("--base", default=os.environ.get("WRITESONIC_BASE_URL", "https://api.writesonic.com"))
ap.add_argument("--key", default=os.environ.get("WRITESONIC_API_KEY", ""))
ap.add_argument("--check-only", action="store_true")
args = ap.parse_args(argv)
history = json.loads(Path(args.history_json).read_text(encoding="utf-8"))
errs = validate_history(history)
if errs:
print("INVALID")
for e in errs:
print("-", e)
return 1
print("history OK", len(history), "turns")
if args.check_only:
return 0
if not args.key:
print("设置 WRITESONIC_API_KEY", file=sys.stderr)
return 2
if not (1 <= len(args.message) <= 600):
print("message 长度须在 1..600", file=sys.stderr)
return 2
r = chat(args.base, args.key, args.message, history)
print(r.get("message") or r)
return 0
if __name__ == "__main__":
raise SystemExit(main())
第 2 步:测试
from pathlib import Path
import json
import subprocess
import sys
import time
from history_check import validate_history
HERE = Path(__file__).resolve().parent
def test_validate():
h = json.loads((HERE / "sample_history.json").read_text())
assert validate_history(h) == []
assert validate_history([{"foo": 1}])
def test_mock_memory():
port = 18991
proc = subprocess.Popen(
[sys.executable, str(HERE / "mock_writesonic.py"), "--port", str(port)],
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
)
try:
time.sleep(0.3)
r = subprocess.run(
[
sys.executable,
str(HERE / "history_check.py"),
str(HERE / "sample_history.json"),
"--base",
f"http://127.0.0.1:{port}",
"--key",
"ws-test-key",
"--message",
"请给三个要点",
],
capture_output=True,
text=True,
check=True,
)
assert "hist=3" in r.stdout
finally:
proc.terminate()
proc.wait(timeout=5)
第 3 步:运行
history OK 3 turns
.. [100%]
2 passed in 0.39s
怎么检查结果?
--check-only不联网。- 正式调用前确认历史不含密钥或隐私。
调试示例
把系统提示塞进 history。 格式允许,但容易泄露内部指令。校验器只查结构;内容审查要另做。
常见问题
| 现象 | 原因 | 处理 |
|---|---|---|
| 历史过长 | 超限 | 截断早期轮次 |
| is_sent 用 0/1 | 类型错误 | 必须 bool JSON |
| 无记忆 | enable_memory false | 检查字段 |
模板规则
- 历史里不要放密钥。
- 隐私对话勿默认持久化。
- 续写结果仍要人工审。
项目资源
| 用途 | 地址 |
|---|---|
| Chatsonic 参考(官方) | https://docs.writesonic.com/reference/chatsonic_v2businesscontentchatsonic_post-1 |
| Chatsonic API(官方) | https://docs.writesonic.com/docs/chatsonic-api |
| API key(官方) | https://docs.writesonic.com/reference/finding-your-api-key |
记忆是上下文,不是免审金牌。