带引用的检索问答
题面
带引用的检索问答
题目描述
检索增强生成(RAG)系统的输出必须可溯源:答案要能在给定语料里找到依据,
并且明确指出依据来自哪几篇文档。本题要求你实现这样一个 Agent:
- 通过评测端提供的能力检索本地语料(Solution 容器无网,所有检索都在评测端完成);
- 依据检索到的片段作答;
- 给出引用列表(支撑答案的文档 id)。
评测端内置一份 16 篇文档的小型语料(虚构的「星槎推理平台」运维知识库),
问题覆盖单文档事实、跨文档多跳、长噪声问题,以及语料中根本没有答案的问题。
只依赖语料,不要依赖常识:语料是虚构的,任何未经检索的作答都无法命中要点。
语料内容也不能被猜测或背诵——你只能通过search/get_document读到它。
提交
在 main.py 中实现(签名不可更改):
def solve(question: str) -> str:
"""返回一行 JSON:{"answer": "<字符串>", "citations": ["<doc_id>", ...]}"""
评测端以 solve(question) 调用(question 为问题原文,可能为空串,也可能是长文本)。
可用能力
| 能力 | 签名 | 返回 |
|---|---|---|
search |
search(query: str, top_k: int = 5) |
[{"doc_id", "title", "snippet", "score"}, ...],按相关度降序,最多 top_k 条(top_k 截断到 1..20) |
get_document |
get_document(doc_id: str) |
{"doc_id", "title", "text"};未知 id 返回 {"error": "unknown_document"} |
list_documents |
list_documents() |
[{"doc_id", "title"}, ...](语料清单,不含正文) |
llm_complete |
llm_complete(messages, params=None) |
OpenAI 兼容响应;正文在 response["choices"][0]["message"]["content"] |
调用方式(Solution 侧):
from noj_solution_sdk import call_capability
hits = call_capability("search", "批处理窗口是多少毫秒", 6)
doc = call_capability("get_document", hits[0]["doc_id"])
resp = call_capability("llm_complete", [
{"role": "system", "content": "只能依据片段作答,输出 JSON。"},
{"role": "user", "content": "..."},
], {"temperature": 0})
query为空/空白 →search返回空列表(不报错)。params只接受temperature/top_p/max_tokens/stop等采样参数,
其余字段会被丢弃;messages的role只允许system/user/assistant。- Solution 容器无网,也拿不到任何
NOJ_LLM_*环境变量:上面 4 个能力是与评测端交互的唯一通道。 - 除
llm_complete外,能力调用没有次数限制(受单用例时限约束)。
输出格式
一行 JSON 对象(允许在结果行之前打印调试信息):
{"answer": "批处理窗口默认 40 毫秒,单批最多合并 64 条请求。", "citations": ["D02"]}
| 字段 | 类型 | 说明 |
|---|---|---|
answer |
字符串 | 答案正文;不得为空或纯空白,最长 4000 字符(超出截断) |
citations |
字符串数组 | 支撑答案的 doc_id 列表;最多 32 条;重复项会被去重(不额外扣分、也不增加召回) |
评分(满分 100)
正式得分只来自隐藏用例,各用例等权。单用例得分:
记 P = 精确率 = |引用 ∩ 金标| / |引用|,R = 召回率 = |引用 ∩ 金标| / |金标|,
F1 = 2PR / (P + R),A ∈ [0,1] = 答案要点覆盖(由 LLM 评审给出 0–100 分后归一化):
| 情况 | 单用例得分 |
|---|---|
| 输出不合法(非 JSON 对象 / 缺字段 / 类型错 / 引用超过 32 条) | 0 |
本用例从未调用 search 或 get_document |
0 |
answer 为空或纯空白 |
0 |
| 引用了语料中不存在的 doc_id(幻觉引用) | 0 |
可回答用例:citations 为空 |
0 |
可回答用例:P < 0.5(堆砌引用,答案不予采信) |
0 |
| 其余可回答用例 | 100 × (0.5·A + 0.5·F1) |
语料无法回答的用例:citations 非空 |
0 |
语料无法回答的用例:citations 为空 |
100 × A |
| 本用例内求解过程抛出异常(含入口未实现) | 0(只影响该用例) |
| 本用例超出单用例时限 | 0(只影响该用例) |
失败只结算到该用例:某一条用例抛异常或超时,其余用例照常评测,不会让整份提交
落为 error(也就不会因为一条用例崩溃而丢掉其余用例的得分)。
要点覆盖 A 的口径:覆盖全部标准要点给 1.0,覆盖一半给 0.5,与要点无关给 0;
语义等价即可,不要求逐字一致。
"语料无法回答"的用例(citations 必须为空数组)采用二值判定:作答必须同时
(a) 说明语料中没有该信息、无法据此作答;(b) 给出检索依据——必须明确说明你已检索
语料库中的文档、并给出语料规模(即文档总数)。只回一句"无法回答/不知道"记 0;
编造具体事实记 0。语料规模可通过 list_documents() 获取;空问题(question 为空)
同样要求给出文档总数。
引用与得分的关系(示例,A = 1.0)
| 金标引用 | 你的 citations |
P | R | F1 | 得分 |
|---|---|---|---|---|---|
{X} |
[X] |
1.000 | 1.000 | 1.000 | 100.00 |
{X} |
[X, Y] |
0.500 | 1.000 | 0.667 | 83.33 |
{X, Y} |
[X, Y] |
1.000 | 1.000 | 1.000 | 100.00 |
{X, Y} |
[X] |
1.000 | 0.500 | 0.667 | 83.33 |
{X} |
[X, Y, Z] |
0.333 | 1.000 | 0.500 | 0(精确率低于 0.5) |
{X} |
全部 16 篇 | 0.063 | 1.000 | 0.118 | 0(堆砌引用) |
{X} |
[X, "D99"](不存在) |
— | — | — | 0(幻觉引用) |
{X} |
[] |
— | — | — | 0(无引用等于无依据) |
可见:宁少勿滥。金标只有一篇时,引到第 3 篇就会因精确率低于门槛而整题归零;
引到第 2 篇(精确率 0.5)也已经丢掉 16.67 分。
题面示例(可见用例,参与评测但不计分)
示例 1
问题:平台内部评测基准的总分是如何加权的?
合格输出:
{"answer": "内部基准包含长文问答、代码生成、工具调用与安全拒答四个集合,总分取四个集合的加权平均,权重分别为 0.3、0.3、0.3 与 0.1。", "citations": ["D11"]}
示例 2
问题:单个租户命名空间的配额上限是多少?
合格输出:
{"answer": "命名空间配额限制最大并发请求数为 32,最大常驻模型数为 4。", "citations": ["D13"]}
示例 3(语料无法回答)
问题:星槎平台的月度服务费用是多少?
合格输出:
{"answer": "已检索语料库中全部 16 篇文档,均未包含平台月度服务费用的信息,因此无法据此作答。", "citations": []}
数据范围与限制
- 语料:16 篇短文档;评测端保证非空,但你的代码仍应能处理"检索无结果"的情况。
- 问题长度:0 – 数千字符;空问题必须仍然返回合法 JSON(
citations: []+ 无法作答说明)。 - 单用例时限:30 秒;评测总时限:300 秒(与平台
JUDGE_MAX_EVALUATOR_TIME_MS
默认值一致;若部署调高该上限,本题目可相应放宽)。 - 内存限制:512 MB。
- LLM 预算:整场评测
llm_complete最多 40 次(18 个用例,平均每例约 2 次);
超限即 0 分,请把 LLM 用在"生成答案、决定引用"上,不要用它做穷举。 - 平台侧对 LLM 的总调用(含评测端评审)另有额度上限,超限会使评测失败。
提示
- 检索能力本身很轻量(BM25 风格),多试几个查询词通常比只查一次更稳;
也可以先list_documents()看看语料有哪些主题。 - 引用必须来自你真的读过的文档:把模型给出的 doc_id 与检索结果取交集,
是避免幻觉引用最简单有效的做法。 - 语料中不少文档主题相邻(例如"限流"与"重试"、"上线清单"与"注册规范"),
引错一篇就会因为精确率下降而丢分。
题解与讨论
查看本题的公开题解,或在通过后分享思路。