Files
duocthu/apps/ai-service/rag/prompt.py
T

78 lines
3.1 KiB
Python

"""The answer contract given to the generator, and the schema it must fill.
This is domain policy, not infrastructure: it states what a grounded answer to
a clinician is allowed to contain. It lives here so it can be read, reviewed
and tested without an SDK, and so swapping the provider cannot silently change
what the model was told.
The audience is doctors and pharmacists, so the instructions ask for the
book's own wording and its own precision rather than a simplification.
"""
from __future__ import annotations
from dataclasses import dataclass
SYSTEM_PROMPT = """\
Bạn trình bày lại nội dung Dược thư Quốc gia Việt Nam cho bác sĩ và dược sĩ.
Bạn KHÔNG phải nguồn tri thức. Toàn bộ nội dung câu trả lời phải đến từ phần
BẰNG CHỨNG được cung cấp trong tin nhắn này.
Quy tắc bắt buộc:
1. Chỉ dùng thông tin có trong BẰNG CHỨNG. Không thêm kiến thức y khoa từ
bên ngoài, kể cả khi bạn chắc chắn nó đúng.
2. Mọi con số — liều, nồng độ, khoảng thời gian, tuổi, cân nặng — phải được
CHÉP NGUYÊN VĂN từ BẰNG CHỨNG, đúng từng ký tự, kể cả dấu phẩy thập phân.
Không làm tròn, không đổi đơn vị, không quy đổi.
3. Mỗi ý phải gắn số nguồn dạng [n], với n là số thứ tự đoạn bằng chứng.
4. Nếu BẰNG CHỨNG không đủ để trả lời, nói rõ là không đủ. Đó là câu trả lời
hợp lệ, không phải thất bại.
5. Giữ nguyên thuật ngữ chuyên môn của sách. Không diễn giải cho người
không chuyên.
Viết gọn. Trả lời đúng điều được hỏi, không mở rộng phạm vi."""
ANSWER_SCHEMA = {
"type": "object",
"properties": {
"answer": {
"type": "string",
"description": (
"Câu trả lời cho bác sĩ/dược sĩ, mỗi ý gắn [n] chỉ nguồn. "
"Mọi con số chép nguyên văn từ bằng chứng."
),
},
"evidence_sufficient": {
"type": "boolean",
"description": "false nếu bằng chứng không đủ để trả lời câu hỏi.",
},
},
"required": ["answer", "evidence_sufficient"],
"additionalProperties": False,
}
@dataclass(frozen=True)
class GenerationRequest:
system: str
user: str
schema: dict
def build_request(question: str, evidence_texts: tuple[str, ...]) -> GenerationRequest:
"""The prompt for one question over one ordered evidence list.
Evidence is numbered from 1 so the model's `[n]` markers and the citation
list the API returns share one index space; `grounding.verify` rejects any
marker outside it.
"""
if not evidence_texts:
raise ValueError("cannot build a grounded prompt with no evidence")
blocks = "\n\n".join(
f"[{index}] {text}" for index, text in enumerate(evidence_texts, start=1)
)
user = f"BẰNG CHỨNG:\n\n{blocks}\n\nCÂU HỎI: {question}"
return GenerationRequest(system=SYSTEM_PROMPT, user=user, schema=ANSWER_SCHEMA)