AI 工具

用 Chatsonic history_data 校验多轮对话上下文

打开 enablememory 时需传 historydata:元素含 issent(bool)与 message。这篇先校验历史格式,再向 mock 发一轮续写。

BINBHO · 图文教程

打开 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

记忆是上下文,不是免审金牌。