把本地文档塞进对话前,先做字符上限守卫并包装「根据文档回答,不知道就说不知道」提示,降低幻觉与超限浪费。
先看清成品
- 短文档 OK 并生成 messages;超长文档 INVALID。
- 3 项 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 通过。
做完之后你会得到什么?
| 成果 | 说明 |
|---|---|
doc_prompt.py |
提示包装 |
sample_doc.txt |
样例 |
test_104c.py |
测试 |
需要准备哪些环境?
| 项目 | 说明 |
|---|---|
| Python | 3.10+ |
关键事实
| 事实 | 来源 |
|---|---|
| 真实上限以模型上下文 token 计;本教程用字符软限制 | 重要 |
| 文档问答仍需引用核对 | 实践 |
| 勿上传无授权机密文档到第三方 API | 合规 |
文件结构
104-mistral-le-chat-C-src/
├── doc_prompt.py
├── sample_doc.txt
└── test_104c.py
第 1 步:包装脚本
#!/usr/bin/env python3
"""Build a Mistral chat prompt from a local document with size guards."""
from __future__ import annotations
import argparse, json, sys
from pathlib import Path
# Soft tutorial limit (chars); real model limits are in tokens — check docs.
DEFAULT_MAX = 12000
def build(doc: str, question: str, max_chars: int) -> dict:
errs = []
if not question.strip():
errs.append("question 为空")
if len(doc) > max_chars:
errs.append(f"文档 {len(doc)} 字符超过上限 {max_chars},请摘要或切片")
if not errs:
content = f"根据下列文档回答问题。若文档未包含答案,请明确说不知道。\n\n---\n{doc}\n---\n\n问题:{question}"
return {"ok": True, "messages": [{"role": "user", "content": content}], "chars": len(doc)}
return {"ok": False, "errors": errs, "chars": len(doc)}
def main(argv=None) -> int:
ap = argparse.ArgumentParser(description=__doc__)
ap.add_argument("doc")
ap.add_argument("question")
ap.add_argument("--max-chars", type=int, default=DEFAULT_MAX)
ap.add_argument("-o", "--out", default="")
args = ap.parse_args(argv)
doc = Path(args.doc).read_text(encoding="utf-8")
result = build(doc, args.question, args.max_chars)
text = json.dumps(result, ensure_ascii=False, indent=2)
if args.out:
Path(args.out).write_text(text + "\n", encoding="utf-8")
print(text)
return 0 if result.get("ok") else 1
if __name__ == "__main__":
raise SystemExit(main())
第 2 步:测试
from pathlib import Path
import subprocess, sys
from doc_prompt import build
HERE = Path(__file__).resolve().parent
def test_ok():
r = build((HERE/"sample_doc.txt").read_text(), "开封的耳机能退吗?", 12000)
assert r["ok"] and "messages" in r
def test_too_big():
r = build("x" * 100, "q", 50)
assert not r["ok"]
def test_cli():
r = subprocess.run([sys.executable, str(HERE/"doc_prompt.py"), str(HERE/"sample_doc.txt"), "退货几天?"], capture_output=True, text=True)
assert r.returncode == 0
第 3 步:运行
$ python doc_prompt.py
{
"ok": true,
"messages": [
{
"role": "user",
"content": "根据下列文档回答问题。若文档未包含答案,请明确说不知道。\n\n---\nAcme 退货政策:自签收起 30 日内可退,商品需未使用且包装完整。电子产品开封后仅保修,不支持无理由退货。\n\n---\n\n问题:开封的耳机能退吗?"
}
],
"chars": 56
}
$ pytest -q
... [100%]
3 passed in 0.04s
怎么检查结果?
- OK 含 messages[0].content。
- 超限返回 errors。
调试示例
整本 PDF 糊进去。 软限制会拒绝。先摘要或按章节切片,再多轮问。
常见问题
| 现象 | 原因 | 处理 |
|---|---|---|
| 先转文本 | 再喂本脚本 | |
| 多文件 | 自行合并/检索 | 本篇单文件 |
| 与 Vibe 上传 | 产品功能不同 | API 需自建 |
模板规则
- 无授权文档不上传。
- 超长先切片。
- 答案要可追溯到原文。
项目资源
| 用途 | 地址 |
|---|---|
| Quickstart(官方) | https://docs.mistral.ai/getting-started/quickstart |
| Docs(官方) | https://docs.mistral.ai/ |
| Mistral 官网 | https://mistral.ai/ |
文档进模型之前,先过体积与授权两道闸。