跳转到主要内容
P1主题库中等

带引用的检索问答

题面

在编辑器打开

带引用的检索问答

题目描述

检索增强生成(RAG)系统的输出必须可溯源:答案要能在给定语料里找到依据,
并且明确指出依据来自哪几篇文档。本题要求你实现这样一个 Agent:

  1. 通过评测端提供的能力检索本地语料(Solution 容器无网,所有检索都在评测端完成);
  2. 依据检索到的片段作答;
  3. 给出引用列表(支撑答案的文档 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 与检索结果取交集,
    是避免幻觉引用最简单有效的做法。
  • 语料中不少文档主题相邻(例如"限流"与"重试"、"上线清单"与"注册规范"),
    引错一篇就会因为精确率下降而丢分。
登录 后即可提交代码

题解与讨论

查看本题的公开题解,或在通过后分享思路。

查看全部
题解加载中…