AI 工具

Mistral API:用 Chat Completions 构建对话客户端

Vibe(原 Le Chat)背后的开发接口是 POST https://api.mistral.ai/v1/chat/completions,Bearer 认证。这篇客户端 + mock 演示最小对话。

BINBHO · 图文教程

Vibe(原 Le Chat)背后的开发接口是 POST https://api.mistral.ai/v1/chat/completions,Bearer 认证。这篇客户端 + mock 演示最小对话。

先看清成品

  • mock 回显用户消息。
  • 官方无效密钥 HTTP 401。
  • 1 项 pytest 通过。

合规提醒:面向用户的助手原名 Le Chat,官方称已更名为 Vibe(入口与账号保留,以官网公告为准);开发调用走 Mistral API(Studio 管理密钥)。密钥勿入库;生成内容需人工审,避免违法与侵权输出。免费额度与价格以 Studio/官网为准,本文不编造。

运行说明:无正式 MISTRAL_API_KEY。A/B 用本地 mock;对官方 api.mistral.ai 无效密钥实测 HTTP 401。C 为文档粘贴体积守卫。2026-10-23(Asia/Shanghai)pytest 通过。

做完之后你会得到什么?

成果 说明
mistral_chat.py 客户端
mock_mistral.py 假服务
test_104a.py 测试

需要准备哪些环境?

项目 说明
Python 3.10+
环境变量 MISTRAL_API_KEY;可选 MISTRAL_BASE_URL / MISTRAL_MODEL

关键事实

事实 来源
Chat Completions 路径 /v1/chat/completions,Authorization: Bearer 官方 API
Studio 可创建 API Key;免费模式说明以官网为准 产品说明
消费级 Vibe 与 API 计费/配额可能不同 定位

文件结构

104-mistral-le-chat-A-src/
├── mistral_chat.py
├── mock_mistral.py
└── test_104a.py

第 1 步:客户端

#!/usr/bin/env python3
"""Mistral chat completions client (API behind Vibe / Le Chat products)."""
from __future__ import annotations
import argparse, json, os, sys, urllib.error, urllib.request

def chat(base, key, model, message, system=None):
    msgs = []
    if system:
        msgs.append({"role": "system", "content": system})
    msgs.append({"role": "user", "content": message})
    req = urllib.request.Request(
        f"{base.rstrip('/')}/v1/chat/completions",
        data=json.dumps({"model": model, "messages": msgs}).encode(),
        headers={"Authorization": f"Bearer {key}", "Content-Type": "application/json"},
        method="POST",
    )
    try:
        with urllib.request.urlopen(req, timeout=60) 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("message", nargs="?", default="用一句话介绍你自己")
    ap.add_argument("--base", default=os.environ.get("MISTRAL_BASE_URL", "https://api.mistral.ai"))
    ap.add_argument("--key", default=os.environ.get("MISTRAL_API_KEY", ""))
    ap.add_argument("--model", default=os.environ.get("MISTRAL_MODEL", "mistral-small-latest"))
    ap.add_argument("--system", default="")
    args = ap.parse_args(argv)
    if not args.key:
        print("设置 MISTRAL_API_KEY(Studio 可创建;额度以官网为准)", file=sys.stderr)
        return 2
    r = chat(args.base, args.key, args.model, args.message, args.system or None)
    print(r["choices"][0]["message"]["content"])
    return 0

if __name__ == "__main__":
    raise SystemExit(main())

第 2 步:Mock

#!/usr/bin/env python3
"""Mock Mistral chat completions."""
from __future__ import annotations
import json
from http.server import BaseHTTPRequestHandler, HTTPServer

API_KEY = "mistral-test-key"

class H(BaseHTTPRequestHandler):
    def log_message(self, *a):
        pass
    def _send(self, code, obj):
        b = json.dumps(obj).encode()
        self.send_response(code)
        self.send_header("Content-Type", "application/json")
        self.send_header("Content-Length", str(len(b)))
        self.end_headers()
        self.wfile.write(b)
    def do_POST(self):
        if self.path != "/v1/chat/completions":
            self.send_error(404); return
        if self.headers.get("Authorization") != f"Bearer {API_KEY}":
            self._send(401, {"message": "Unauthorized"}); return
        n = int(self.headers.get("Content-Length", 0))
        body = json.loads(self.rfile.read(n) or b"{}")
        msgs = body.get("messages") or []
        last = msgs[-1]["content"] if msgs else ""
        model = body.get("model") or "mistral-small-latest"
        self._send(200, {
            "id": "chatcmpl-mock",
            "model": model,
            "choices": [{"index": 0, "message": {"role": "assistant", "content": f"[{model}] 收到:{last[:200]}"}, "finish_reason": "stop"}],
            "usage": {"prompt_tokens": 10, "completion_tokens": 20, "total_tokens": 30},
        })
    def do_GET(self):
        if self.path != "/v1/models":
            self.send_error(404); return
        if self.headers.get("Authorization") != f"Bearer {API_KEY}":
            self._send(401, {"message": "Unauthorized"}); return
        self._send(200, {"data": [
            {"id": "mistral-small-latest"},
            {"id": "mistral-large-latest"},
            {"id": "open-mistral-nemo"},
        ]})

def main():
    import argparse
    ap = argparse.ArgumentParser(); ap.add_argument("--port", type=int, default=9104)
    args = ap.parse_args()
    HTTPServer(("127.0.0.1", args.port), H).serve_forever()

if __name__ == "__main__":
    main()

第 3 步:测试

import subprocess, sys, time
from pathlib import Path
HERE = Path(__file__).resolve().parent

def test_chat():
    port = 19104
    proc = subprocess.Popen([sys.executable, str(HERE/"mock_mistral.py"), "--port", str(port)], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
    try:
        time.sleep(0.3)
        r = subprocess.run([sys.executable, str(HERE/"mistral_chat.py"), "你好", "--base", f"http://127.0.0.1:{port}", "--key", "mistral-test-key"], capture_output=True, text=True, check=True)
        assert "你好" in r.stdout
    finally:
        proc.terminate(); proc.wait(timeout=5)

第 4 步:运行

$ pytest + mock chat
.                                                                        [100%]
1 passed in 0.36s

$ invalid key api.mistral.ai
HTTP 401
{"detail":"Invalid API Key"}

怎么检查结果?

  • pytest 绿。
  • 正式调用:MISTRAL_API_KEY=... python mistral_chat.py "你好"。

调试示例

401 Invalid API Key。 到 console/Studio 重新复制密钥;确认没有多余空格,也没有把 Vibe 网页 Cookie 当成 API Key。

常见问题

现象 原因 处理
选哪个模型 见 Models 文档 或跑 B 篇 pick
要流式 stream 参数 以 API 文档为准
Le Chat 改名 Vibe API 主机未变

模板规则

  • 密钥保密。
  • 输出人工审。
  • 遵守使用政策。

项目资源

用途 地址
Quickstart(官方) https://docs.mistral.ai/getting-started/quickstart
API 参考(官方) https://docs.mistral.ai/api/
Mistral 官网 https://mistral.ai/

先打通 completions,再接到你的业务提示词。