把 ScholarMuse 和现有助手接在同一条问答链路上,先不要比较哪一侧更聪明,而要先看每一步的原始数据掌握在谁手里。论文检索命中、文献元数据(标题、作者、年份、来源、DOI、摘要片段)这类可以通过接口逐字段核对的内容,交给 ScholarMuse;多轮指代消解、追问、把若干条结果组织成一段可读回答并生成引用编号,留在现有助手。这样划分的原因很实际:两侧各改一半时,出问题的现象往往落在同一处(比如“回答不相关”),但从表面看不出是检索没召回、字段没取到,还是助手组织时漏掉了。
归属判断可以压缩成一句:事实字段从哪来,就由哪一侧负责。论文检索、归并与元数据补全归 ScholarMuse;对话状态、上下文截断、引用编号与总结归现有助手。交界处(检索式构造、返回结构校验、引用列表拼接)必须指定唯一负责方,否则同一故障会同时写进两侧日志,难以定位。规则冲突时,数据可核验性优先于对话连贯性,连贯性优先于实现便利。
把一次完整问答拆成检索、取数、组织三步
拆解的目的不是画架构图,而是让每一步都有明确的输入、输出和可回看的中间结果。一次典型问答通常是:用户提问 → 助手判断是否需要检索 → 构造检索式 → ScholarMuse 召回候选 → ScholarMuse 补全元数据 → 助手把结构化条目写成回答。前三段与后一段的分界,就是本页要划的边界。
| 步骤 | 输入 | 输出 | 建议归属 | 中间结果在哪看 |
|---|---|---|---|---|
| 检索:检索式构造与召回 | 用户原始问题、助手改写后的检索式、过滤条件(年份、语言、文献类型) | 候选 doc_id 列表、命中理由、是否被截断的标记 | 召回与归并归 ScholarMuse;检索式改写可由助手做,但必须落日志 | ScholarMuse 侧检索日志、接口响应中的 items 数量与去重标记 |
| 取数:元数据补全 | doc_id 列表、需要的字段集合 | 结构化条目(标题、作者、年份、来源、DOI、摘要片段),缺失字段需显式标记为空 | ScholarMuse | 接口响应体、字段缺失清单 |
| 组织:对话与总结 | 结构化条目、对话历史、用户当前意图 | 自然语言回答、引用编号与出处列表 | 现有助手 | 助手侧提示词入参快照、最终输出 |
三步里至少要有一处可以打印或回放。通常的做法是让助手在调用 ScholarMuse 时带上同一个 request_id,两侧日志用这个 id 对齐;没有这个 id,一旦回答内容不对,就只能靠猜是哪一侧出了问题。
标出两边都会碰到的那部分工作
边界混乱基本都发生在“看起来两边都能做”的地方。下面几类交界问题有比较固定的表现和出现位置,可以照着去日志里找。
- 上下文截断:助手为控制提示词长度截掉较早的对话轮次,但检索依据里的实体(论文名、作者、年份)恰好落在被截掉的部分,于是 ScholarMuse 收到的检索式本身就缺主语。表现是“换个问法就查得到,原问法查不到”。出现位置在助手 → ScholarMuse 的检索式构造点。
- 字段缺失:ScholarMuse 返回的条目缺 DOI、摘要或作者列表,助手为了让回答完整,用模型记忆补了一个看起来合理但无法核对的值。表现是引用能点开但信息对不上。出现位置在返回结构校验点,也就是助手拿到响应后到拼装提示词之间那一段。
- 条目重复:同一篇论文的预印本与正式版被当成两条记录返回,ScholarMuse 未归并,助手又在拼接引用列表时按标题做了第二次去重,导致正文提到 5 篇、文末列出 4 条。出现位置在引用编号生成阶段。
- 排序不可复现:两侧各自做过相关性排序,最终顺序取决于调用顺序。表现是同一次提问两次回放的引用顺序不同。出现位置在合并结果的那一步。
这些问题的共同点是:单看任何一侧的日志都“没报错”。所以交界处必须有且只有一个负责方,另一侧只做透传或显式标注。
为每条边界写一条判断规则
规则要短到能在评审时直接引用,并且每条配一个正例、一个反例,否则落地时会被解释成任意一种。
- R1:能通过一次检索或元数据接口取到的字段,一律由 ScholarMuse 提供,助手不猜。正例:某条记录缺年份,助手在回答里写“年份未提供”,并保留该条目。反例:在助手提示词里写“若缺少 DOI 可自行补全”。
- R2:依赖多轮对话状态的处理归助手。正例:用户第二轮说“它的实验方法”,助手先还原成具体论文标题,再把它作为检索式发给 ScholarMuse。反例:把原始对话历史整段丢给 ScholarMuse,让它自己判断指代。
- R3:归并与规范标识归 ScholarMuse,展示顺序与引用编号归助手。正例:ScholarMuse 用一个稳定 doc_id 合并预印本与正式版,助手只按返回顺序编号。反例:助手按标题字符串再去重一次。
- R4:截断策略归助手,截断后必须显式告知下游。正例:助手在请求里带上
context_truncated: true,回答中提示“仅依据最近几轮对话检索”。反例:静默截断,让 ScholarMuse 以为收到的是完整意图。
规则冲突时的优先级建议是:① 数据可核验性 → ② 对话连贯性 → ③ 实现成本。也就是说,当助手为了回答顺畅需要猜一个字段时,选择让位:如实说明缺失,而不是补齐。这条优先级最好写进两侧的共同约定里,避免每次改动重新争论。
按分工表回看现有配置
规则确定后,逐项对照现有配置,重点找两类问题:越界配置(本该在另一侧的开关留在了这一侧)和缺失配置(交界处没有任何显式标记)。
逐项对照清单
- 助手侧是否仍保留直连文献库或检索接口的开关?若有,移除或降级为仅调试可用。
- 发给 ScholarMuse 的请求里是否夹带了完整对话历史?应只带改写后的检索式与必要过滤条件。
- 助手提示词中是否含“补全作者/年份/DOI”“可自行推断出处”一类指令?应移除。
- ScholarMuse 响应是否稳定包含
doc_id、title、year,缺失时是否有显式空值标记而不是直接省略键? - 引用编号是否只有一处生成?若两侧都有编号逻辑,保留助手侧。
- 两侧是否都记录了同一个 request_id?
两侧应保留与应移除的配置项
下面是一份通用骨架式配置示例,字段名按实际项目替换,数值需结合环境确认后再定。
assistant:
context:
max_turns: 6 # 示例值,按模型上下文和实际问答长度调整
truncate_strategy: keep_latest
mark_truncated: true # 截断后必须告知下游
retrieval:
direct_db_access: false # 应移除:检索不再由助手直连
search_endpoint: scholar_muse
citations:
numbering: assistant # 应保留:编号由助手生成
dedup: false # 应移除:去重交给 ScholarMuse
prompt:
allow_field_inference: false # 应移除:禁止补全缺失字段
ScholarMuse 一侧对应要保留的是:归并逻辑、doc_id 生成、字段完整性标记、检索日志;要移除的是任何“根据对话历史猜意图”的入参,以及把展示顺序写死的逻辑。
调整后的验证方式
验证不需要压测,用几条构造请求把边界走一遍即可。下面是一个通用的请求与响应校验骨架,接口路径按实际项目替换。
POST /internal/scholar-muse/search
{
"request_id": "qa-0001",
"query": "助手改写后的检索式",
"filters": {"year_from": 2018, "language": "en"},
"limit": 10,
"fields": ["title", "authors", "year", "venue", "doi", "abstract_snippet"],
"context_truncated": false
}
REQUIRED = ("doc_id", "title", "year")
def check(resp):
for it in resp.get("items", []):
missing = [f for f in REQUIRED if not it.get(f)]
if missing:
print(it.get("doc_id"), "missing:", missing)
if resp.get("meta", {}).get("returned", 0) == 0:
print("empty recall, check query:", resp.get("meta", {}).get("query"))
具体要跑的四个回放:用一条已知论文的提问,确认 items 数量与字段完整度符合预期;人为构造一条缺 DOI 的响应,确认助手输出是“未提供”而不是编造;用同一论文的两个版本来测,确认只出现一个引用编号;最后用 request_id 对照两侧日志,确认检索式、返回条目、最终引用三者能串起来。能串起来,说明分工已经落到配置上;串不起来,通常还是某一侧在越界处理交界工作。