Files
wehub-resource-sync 94057c3d3e
PR Test (NPU) / check-changes (push) Has been cancelled
PR Test (NPU) / pr-gate (push) Has been cancelled
PR Test (NPU) / set-image-config (push) Has been cancelled
PR Test (NPU) / stage-b-test-1-npu-a2 (0) (push) Has been cancelled
PR Test (NPU) / stage-b-test-1-npu-a2 (1) (push) Has been cancelled
PR Test (NPU) / stage-b-test-2-npu-a2 (0) (push) Has been cancelled
PR Test (NPU) / stage-b-test-2-npu-a2 (1) (push) Has been cancelled
PR Test (NPU) / stage-b-test-4-npu-a3 (push) Has been cancelled
PR Test (NPU) / stage-b-test-16-npu-a3 (push) Has been cancelled
PR Test (NPU) / multimodal-gen-test-1-npu-a3 (push) Has been cancelled
PR Test (NPU) / multimodal-gen-test-2-npu-a3 (push) Has been cancelled
PR Test (Arm64) / pr-gate (push) Has been cancelled
PR Test (Arm64) / check-changes (push) Has been cancelled
PR Test (Arm64) / build-test (push) Has been cancelled
PR Test (sgl-router) / gate (push) Has been cancelled
PR Test (sgl-router) / tier-1 — lint (push) Has been cancelled
PR Test (sgl-router) / tier-2 — build + test (push) Has been cancelled
PR Test (sgl-router) / tier-3 — docker (placeholder) (push) Has been cancelled
PR Test (sgl-router) / tier-3 — k8s integration (push) Has been cancelled
PR Test (sgl-router) / tier-3 — e2e (push) Has been cancelled
PR Test (sgl-router) / finish (push) Has been cancelled
PR Test (NPU) / single-node-poc (map[name:qwen3_6_27b_w8a8_1p_in64k_out1k_50ms runner:linux-aarch64-a3-2 test_case:test/registered/ascend/performance/qwen3_6_27b/test_npu_qwen3_6_27b_w8a8_1p_in64k_out1k_50ms.py test_type:perf]) (push) Has been cancelled
PR Test (NPU) / pr-test-npu-finish (push) Has been cancelled
PR Test (Xeon) / pr-gate (push) Has been cancelled
PR Test (Xeon) / check-changes (push) Has been cancelled
PR Test (Xeon) / build-test (, xeon-gnr, base-b-test-cpu) (push) Has been cancelled
PR Test (XPU) / check-changes (push) Has been cancelled
PR Test (XPU) / pr-gate (push) Has been cancelled
PR Test (XPU) / stage-a-test-1-gpu-xpu (push) Has been cancelled
PR Test (XPU) / wait-for-stage-a (push) Has been cancelled
PR Test (XPU) / stage-b-test-1-gpu-xpu (push) Has been cancelled
PR Test (XPU) / finish (push) Has been cancelled
CI Model Inventory / build-inventory (push) Has been cancelled
Lint / lint (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark Compilation Check (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark - Manual Policy (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark - Request Processing (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark Summary (push) Has been cancelled
PR Test (SMG) / build-wheel (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on windows (x86_64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on macos (x86_64 - auto) (push) Has been cancelled
PR Test (SMG) / python-unit-tests (push) Has been cancelled
PR Test (SMG) / unit-tests (push) Has been cancelled
PR Test (SMG) / benchmarks (push) Has been cancelled
PR Test (SMG) / chat-completions (push) Has been cancelled
PR Test (SMG) / chat-completions-4gpu (push) Has been cancelled
PR Test (SMG) / e2e (push) Has been cancelled
PR Test (SMG) / docker-build-test (push) Has been cancelled
PR Test (SMG) / k8s-integration (push) Has been cancelled
PR Test (SMG) / finish (push) Has been cancelled
PR Test (SMG) / summarize-benchmarks (push) Has been cancelled
Release SGLang Model Gateway Docker Image / publish (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on macos (aarch64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (aarch64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (x86_64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (aarch64 - musllinux_1_1) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (x86_64 - musllinux_1_1) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / Build SDist (push) Has been cancelled
Release SGLang Model Gateway to PyPI / Upload to PyPI (push) Has been cancelled
Release SGLang Kernels / build-cu129-matrix (aarch64, 12.9, 3.10, arm-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / build-cu129-matrix (x86_64, 12.9, 3.10, x64-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / release-cu129 (push) Has been cancelled
Release SGLang Kernels / build-cu130-matrix (aarch64, 13.0, 3.10, arm-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / build-cu130-matrix (x86_64, 13.0, 3.10, x64-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / release-cu130 (push) Has been cancelled
Release SGLang Kernels / build-rocm-matrix (3.10, 700) (push) Has been cancelled
Release SGLang Kernels / build-rocm-matrix (3.10, 720) (push) Has been cancelled
Release SGLang Kernels / release-rocm700 (push) Has been cancelled
Release SGLang Kernels / release-rocm720 (push) Has been cancelled
Release SGLang Kernels / build-musa43 (43, 3.10) (push) Has been cancelled
Release SGLang Kernels / release-musa43 (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:38:16 +08:00

279 lines
9.5 KiB
Python
Executable File

#!/usr/bin/env python3
"""Chain-of-Verification (CoVe) example for reducing LLM hallucinations with SGLang.
This script implements the "Factored CoVe" pattern from:
Dhuliawala et al. (2023) "Chain-of-Verification Reduces Hallucination in Large Language Models"
https://arxiv.org/abs/2309.11495
The key insight (Factored CoVe): the verification call runs in a **fresh session with
no shared history or KV-cache** from the original answer. This prevents the model from
simply repeating its earlier (possibly hallucinated) answer instead of genuinely checking it.
Flow
----
1. [Draft] Send user query → get initial answer from the model.
2. [Verify] Open a *new, independent* chat (no history) and ask:
"Does this answer actually address the question? If not, say so."
3. [Refine] If the verifier flags a problem, ask it to produce a corrected answer
(still within the same verify session so it has the critic context).
4. [Summarize] (Optional) Ask for a shorter final answer.
Usage
-----
1. Launch an SGLang-compatible server, e.g.:
python -m sglang.launch_server \\
--model-path meta-llama/Llama-3.1-8B-Instruct --port 30000
2. Run this script:
python chain_of_verification.py --prompt "What year did the Titanic sink?"
Or point at a different server / model:
python chain_of_verification.py \\
--base-url http://127.0.0.1:30002/v1 \\
--model moonshot-v1-8k \\
--prompt "Who invented the telephone?"
"""
from __future__ import annotations
import argparse
import sys
from typing import Any
try:
from openai import OpenAI
except ImportError:
print("Missing dependency: pip install openai", file=sys.stderr)
sys.exit(1)
DEFAULT_BASE_URL = "http://127.0.0.1:30000/v1"
DEFAULT_MODEL = None # auto-detected from /v1/models
VERIFY_SYSTEM_PROMPT = (
"You are a strict fact-checker. "
"You will be given a user question and a candidate answer. "
"Decide whether the answer is accurate and directly addresses the question. "
"Reply with exactly one of: PASS or FAIL, followed by a brief reason."
)
REFINE_INSTRUCTION = (
"The previous answer was flagged as inaccurate or off-topic. "
"Please provide a corrected, accurate answer to the original question."
)
SUMMARIZE_INSTRUCTION = (
"Please give a concise, one-paragraph version of the verified answer above."
)
# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------
def resolve_model(client: OpenAI, model: str | None) -> str:
if model:
return model
models = client.models.list()
if not models.data:
raise RuntimeError("Server returned no models from /v1/models")
return models.data[0].id
def chat(
client: OpenAI,
model: str,
messages: list[dict[str, Any]],
max_tokens: int,
temperature: float,
) -> str:
response = client.chat.completions.create(
model=model,
messages=messages,
max_tokens=max_tokens,
temperature=temperature,
)
msg = response.choices[0].message
# Reasoning models (e.g. Kimi-K2.5, Qwen3) may return the final answer in
# `content` and the chain-of-thought in `reasoning_content`. In some
# configurations (greedy / temperature=0) `content` can be empty while the
# useful text lives in `reasoning_content`, so fall back to it.
content = msg.content or ""
if not content.strip():
content = getattr(msg, "reasoning_content", None) or ""
return content
# ---------------------------------------------------------------------------
# CoVe pipeline
# ---------------------------------------------------------------------------
def chain_of_verification(
client: OpenAI,
model: str,
user_query: str,
max_tokens: int = 1024,
temperature: float = 0.6,
summarize: bool = False,
verbose: bool = True,
) -> str:
"""Run the Factored Chain-of-Verification pipeline.
Parameters
----------
client: OpenAI-compatible client pointing at an SGLang server.
model: Model identifier.
user_query: The question or request from the user.
max_tokens: Token budget per call.
temperature: Sampling temperature.
summarize: If True, append an optional summarization step (Step 4).
verbose: Print intermediate results.
Returns
-------
The final (verified / refined) answer string.
"""
def _log(title: str, text: str) -> None:
if verbose:
print(f"\n{'=' * 60}")
print(f"[{title}]")
print(text)
# ------------------------------------------------------------------
# Step 1 — Draft: generate the initial answer
# ------------------------------------------------------------------
draft_messages: list[dict[str, Any]] = [
{"role": "user", "content": user_query},
]
draft_answer = chat(client, model, draft_messages, max_tokens, temperature)
_log("Step 1 · Draft answer", draft_answer)
# ------------------------------------------------------------------
# Step 2 — Verify (Factored): fresh session, no shared history/cache
#
# The verification session deliberately starts from scratch so the
# model cannot attend to the draft answer's token embeddings. This
# mirrors the "factored" variant in the CoVe paper, which consistently
# outperforms the joint variant.
# ------------------------------------------------------------------
verify_messages: list[dict[str, Any]] = [
{"role": "system", "content": VERIFY_SYSTEM_PROMPT},
{
"role": "user",
"content": (
f"Question: {user_query}\n\n"
f"Candidate answer:\n{draft_answer}\n\n"
"Does this answer accurately and completely address the question?"
),
},
]
verdict = chat(client, model, verify_messages, 256, temperature)
_log("Step 2 · Verification verdict", verdict)
passed = verdict.strip().upper().startswith("PASS")
if passed:
final_answer = draft_answer
_log("Result", "Verification PASSED — using draft answer as final answer.")
else:
# ------------------------------------------------------------------
# Step 3 — Refine: ask the verifier to correct its own critique
#
# We continue in the *verify* session (not the draft session) so the
# model has context about *why* the draft failed.
# ------------------------------------------------------------------
verify_messages.append({"role": "assistant", "content": verdict})
verify_messages.append({"role": "user", "content": REFINE_INSTRUCTION})
refined_answer = chat(client, model, verify_messages, max_tokens, temperature)
_log("Step 3 · Refined answer", refined_answer)
final_answer = refined_answer
# ------------------------------------------------------------------
# Step 4 — Summarize (optional)
# ------------------------------------------------------------------
if summarize:
summarize_messages: list[dict[str, Any]] = [
{"role": "user", "content": user_query},
{"role": "assistant", "content": final_answer},
{"role": "user", "content": SUMMARIZE_INSTRUCTION},
]
summary = chat(client, model, summarize_messages, 256, temperature)
_log("Step 4 · Summary (optional)", summary)
final_answer = summary
return final_answer
# ---------------------------------------------------------------------------
# CLI
# ---------------------------------------------------------------------------
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(
description="Chain-of-Verification (CoVe) hallucination reduction demo for SGLang",
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
)
parser.add_argument(
"--base-url",
default=DEFAULT_BASE_URL,
help="SGLang OpenAI-compatible API base URL",
)
parser.add_argument(
"--model",
default=DEFAULT_MODEL,
help="Model name; auto-detected from /v1/models if omitted",
)
parser.add_argument(
"--prompt",
default="Who invented the telephone and in what year?",
help="User question / prompt",
)
parser.add_argument("--max-tokens", type=int, default=10240)
parser.add_argument("--temperature", type=float, default=0.6)
parser.add_argument(
"--summarize",
action="store_true",
help="Append an optional summarization step (Step 4)",
)
parser.add_argument(
"--quiet",
action="store_true",
help="Suppress intermediate step output; only print the final answer",
)
return parser.parse_args()
def main() -> None:
args = parse_args()
client = OpenAI(api_key="EMPTY", base_url=args.base_url)
try:
model = resolve_model(client, args.model)
except Exception as exc:
print(f"Failed to connect to SGLang server: {exc}", file=sys.stderr)
print(f" Check server at {args.base_url}", file=sys.stderr)
sys.exit(1)
print(f"Model : {model}")
print(f"Query : {args.prompt}")
final = chain_of_verification(
client=client,
model=model,
user_query=args.prompt,
max_tokens=args.max_tokens,
temperature=args.temperature,
summarize=args.summarize,
verbose=not args.quiet,
)
if args.quiet:
print(final)
if __name__ == "__main__":
main()