可编程 Scorer 使用指南¶
程序执行供本微服务 HTTP 与 CLI
--direct使用;第一阶段另提供显式 HTTP 自然语言生成接口,默认关闭。不提供独立/v1/rank或候选导出。qatlasd/网页的端到端接入需另行验证。
1. 三种排序入口¶
ranking |
行为 |
|---|---|
缺省或 null |
当前固定公式和默认权重 |
|
原线:服务调用配置的 LLM 选择三个权重;失败仍回退默认并记 intent 错误 |
|
执行调用方提交的评分程序,不调用意图 LLM |
ranking 是模式,scorer 是程序,不是一个权重参数。外部 Agent 可以阅读能力说明后编写程序,也可显式调用下文的生成接口;/v1/search 不会在 scorer 缺省时替它生成程序。
agent: true 仍只表示对最终结果生成总结;可以与 scorer 模式并用。
2. 快速使用¶
HTTP¶
以下命令假设微服务已经部署且可达;将地址和令牌替换为自己的值。配置了服务令牌时,搜索与能力接口都要求 Bearer 鉴权。
curl -s http://localhost:8600/v1/scoring/capabilities \
-H 'Authorization: Bearer <service_token>'
curl -s http://localhost:8600/v1/search \
-H 'Authorization: Bearer <service_token>' \
-H 'Content-Type: application/json' \
-d '{
"query": "surface code threshold",
"sources": ["arxiv", "openalex", "semantic_scholar"],
"ranking": "scorer",
"scorer": {
"language": "qatlas-expr-v1",
"filter": "lexical >= 0.6",
"score": "log1p(coalesce(citations, 0))"
},
"explain": true,
"max_results": 10
}'
CLI¶
从项目根目录执行:
# 仅显示本地语法、特征和限额,不需要查询、配置或网络
qatlas-search --list-scoring-capabilities
# 实际检索:读取本地 JSON 程序,直连已选数据源
qatlas-search --direct "surface code threshold" \
--ranking scorer --scorer-file docs/examples/scorers/classic.json --json
qatlas-search --direct "surface code threshold" \
--ranking scorer --scorer-file docs/examples/scorers/recent.json --explain --json
# 原有 LLM 配权重模式仍可使用;需服务 YAML 中配置 agent
qatlas-search --direct "surface code threshold 最新进展" --ranking auto --json
--max-results仍为 CLI direct 的每源预算;--top为展示条数。scorer 模式要求二者为正数。scorer 文件必须是 UTF-8 JSON,整个文件最多 4 KiB(含空白);读取在 JSON 解析前受限。
scorer 模式的
--json使用非缩进格式,防止美化输出扩大解释字节预算;旧默认/auto 输出格式不变。CLI 默认的 server 路径走 qatlasd,其 scorer/file/explain 参数在请求及鉴权解析前明确报不支持。不要用
--server指向本微服务来绕过这一点,两者协议不同。CLI direct 的
--agent不生成总结,沿用旧语义;需要总结用微服务 HTTP。
自然语言生成(第一阶段)¶
POST /v1/scoring/generate 只把自然语言要求转换为 qatlas-expr-v1,并使用现有编译器检查语法、类型和程序资源限制。它不检索论文、不执行生成的 scorer、不自动搜索或优化。检查程序、模型解释与警告后,由调用方把返回的 scorer 原样放入现有 /v1/search 请求,并指定 ranking: "scorer";生成成功不等于运行时定义域安全或满足全部自然语言语义。
请求与响应¶
请求对象只允许 query、requirements 两个必填字段;均为严格字符串,原始长度各为 1..4000 字符,不允许 null、类型转换或全空白,验证后去除首尾空白。不接受 api_keys 等额外字段。整个 UTF-8 JSON 请求体最多 64 KiB(含空白及分块传输);沿用解析前的编码及 JSON 嵌套保护。配置服务令牌时,先检查 Bearer 鉴权再读取请求体。
curl -s http://localhost:8600/v1/scoring/generate \
-H 'Authorization: Bearer <service_token>' \
-H 'Content-Type: application/json' \
-d '{"query":"surface code threshold","requirements":"只保留 lexical >= 0.6 的论文,按 log1p 引用数降序,缺失引用数按 0 处理"}'
成功返回 HTTP 200:
字段 |
契约 |
|---|---|
|
编译通过的规范化程序: |
|
以 |
|
模型警告字符串列表,最多 8 条,每条非空且最多 500 字符 |
|
与现有编译器一致的规范化程序哈希 |
|
当前特征版本 |
|
|
失败始终使用 {"detail":{"code":"…","message":"…","field":"…"},"usage":{"llm_tokens":N}};field 可省略。错误不回显原请求、密钥或原始上游错误。已知用量即使后续修复失败或超时也保留;请求校验、预鉴权和准入拒绝均为 0。
HTTP |
常见 code / 原因 |
|---|---|
400 |
|
401 |
|
413 |
|
422 |
|
429 |
|
502 |
|
503 |
|
504 |
|
作者、期刊等不可用元数据不能转换成现有六特征公式。服务对 requirements 中常见作者/期刊/会议/机构/标识符/开放获取/影响因子等关键词直接 fail closed,不调用模型;该保守检测可能要求用户改写。对其他不支持或含糊要求,提示词要求模型明确返回 unsupported_requirements 或 clarification,不得静默丢弃约束或自造特征;用户需修改要求后重新请求。关键词检查和编译都不是完整的自然语言语义验证,仍需人工确认程序,不能把 summary 当作保证。
内置变量与错误拒绝¶
features 中的键不是待用户定义的概念,而是服务器已经提供、可在 filter 和 score
中直接引用的标量变量。score: "lexical" 是完整合法的评分表达式,不会再自动叠加
引用数、年份或默认评分公式。year 是可用的年份变量;未知年份使用 coalesce
处理,并不意味着系统不支持年份过滤。例如“只要2020年之后,仅按 lexical 排序”可写为:
{"language":"qatlas-expr-v1","filter":"coalesce(year, 0) > 2020","score":"lexical"}
这里不包含2020年,并排除未知年份;若包含2020年则使用 >= 2020。
生成提示词明确区分引用已经计算好的 lexical/phrase 信号与不支持的“新增原始文本
谓词/自造特征”,并包含相关性排序、严格年份边界及包含边界的完整示例。示例在离线
测试中经过模型输出 schema、编译和合成论文执行验证,而不是遇到失败时替换模型输出
的兜底规则。模型的 unsupported_requirements 解释仍是模型判断,不是编译器对系统
能力的权威判定;若模型拒绝,接口不会忽略要求或悄悄改用默认 scorer。
生成语义验收(显式选择真实模型)¶
容器中可直接运行:
# 默认纯离线:不读模型配置、不触网、不消耗模型 tokens。
docker exec qatlas-search python -m qatlas_search.scorer_acceptance
# 明确选择真实模型:读取现有配置,提交固定的中文回归需求。
# 会产生模型费用;这是运维验收,不经过网页的用户额度计量。
docker exec qatlas-search python -m qatlas_search.scorer_acceptance --live
固定需求为:“只要2020年之后,最终分数直接使用系统已有的 lexical 特征,按分数降序 排序,不加入引用数或其他加分项。”工具在生成后验证 168 个合成样本:严格排除 2019/2020/未知年份、保留2021年及以后;零/部分/全部词项相关性保持各自 lexical 分数, 改变引用数、年份和来源数不能引入加分。逐条命中及全量排序均与基准程序比较。
输出 JSON 含 passed、版本、机器 code 和已知 usage.llm_tokens;成功可显示
编译后的 scorer。失败返回非零退出码,不打印 key、base URL、模型原始解释或异常
堆栈。离线 passed:true 不代表真实模型生成成功。--live 沿用现有最多两次
completion、30秒默认总限时和 token/响应预算,不添加外层重试,不检索论文或入库。
即使真实模式通过,也只是固定需求的有限样本语义验收,不是所有需求/输入的证明。
启用与配置¶
scorer_generation_enabled 默认 false,独立于 agent_enabled。复用 agent_base_url、agent_api_key、agent_model,三者均须在 strip() 后非空;已有嵌套 agent: {base_url, api_key, model} 也会映射到这三个字段。仅开启旧 agent 不会开启生成;关闭旧 agent 也不妨碍单独开启生成。HTTP capabilities 增加 generation_available 表示上述条件是否满足,不代表上游连通性验证。
使用 DeepSeek 官方 endpoint 时,规则生成会自动采用非思考模式:仅当 agent_base_url 的 URL hostname 在大小写与末尾点规范化后精确等于 api.deepseek.com,每次 completion(含一次 repair)才在请求 JSON 顶层附加 thinking: {"type": "disabled"};不添加 reasoning_effort。官方根地址 https://api.deepseek.com 与 /v1 地址及末尾斜杠均适用,无需更换已有 API key 或 model。这不是按模型名字判断:其他 OpenAI-compatible 服务、第三方网关或代理 host 即使使用 deepseek-v4-pro,也不附加该字段,保留其默认行为。此调整仅限规则生成,原 Agentic 结果总结(agent: true)请求保持不变。参数依据:DeepSeek 官方 Thinking Mode 文档。
生成提示词使用 capabilities 的紧凑投影,仍保留全部可用特征/函数、nullable 与定义域约束,以及拒绝不支持要求和请求 clarification 的安全契约;公开 capabilities 不缩减。
以下字段均平铺于 YAML 的 search: 下,不使用嵌套 scoring::
字段 |
默认 |
允许范围 / 含义 |
|---|---|---|
|
|
布尔值,显式开启生成 |
|
|
1..32,生成请求并发槽 |
|
|
1..64,全部 |
|
|
1..120 秒,两次 completion 共用的总超时 |
|
|
64..4096,每次 completion 输出 token 上限 |
|
|
1024..65536,整次生成总输入 + 输出 token 预算 |
|
|
1024..262144,每次上游原始响应字节上限,默认 64 KiB |
管理页 manifest 的 scoring 分组展示这些字段与说明;PUT /v1/admin/config 校验同样边界,拒绝 NaN/Inf。修改写回配置后必须重启服务,不会热更新预算或并发槽。生成通过运行时依赖 httpx 异步请求 OpenAI 兼容 /chat/completions,不依赖 OpenAI SDK。
有界生成与计量¶
每次生成最多 2 次模型调用:一次初稿,只有输出 JSON/schema/DSL 无效时允许单次 repair。不对网络错误、不支持要求或 clarification 循环重试;修复不延长总超时,也不另开预算。
默认仍为 30 秒整次生成总超时,输出/总 token 及响应字节预算不变;这是生成处理阶段的期限,不是慢速客户端上传的全链路超时。官方非思考模式与提示词缩短不保证在期限内完成,也不是性能或延迟保证。
每次原始上游响应默认最多 64 KiB;拒绝压缩响应以避免解压膨胀。抽取的模型文本输出另外固定最多 16 KiB UTF-8,并继续受 scorer 4 KiB 及 summary/warnings 限制;这些不是整个 HTTP 成功响应的统一大小限额。
每次调用前,以序列化消息的 UTF-8 字节数加 256 framing 余量,再加本次最大输出 token,进行byte-based 保守预留。以累计预留与上游已知累计用量中的较大者检查剩余总预算;修复携带前次输出,也需重新预留,因此即使第一次调用可执行,也可能没有修复预算。
预留用于准入,不是实际 token 统计。
usage.llm_tokens只累计上游合法报告的total_tokens,缺失时累计已知的prompt_tokens/completion_tokens。未报告的未知 token 不得伪计为估算值;返回 0 或部分用量不代表上游没有成本,无法作为精确账单。已知超支返回 413,但不能撤销已经发生的模型调用。
进程内立即准入¶
默认 /v1/scoring/generate 同时最多 2 个请求,所有 /v1/search(包括默认、auto、scorer、multi)共用另一个上限 4。两组槽独立;即使生成关闭,search 上限仍生效。
纯 ASGI 准入在读取请求体、JSON 解析及同步线程池派发前非阻塞取槽,满额立即 429,不创建等待队列;正常、异常或取消退出均释放槽。它限制的是请求并发,不是每秒速率,也不是后端所有内部任务数;/v1/survey-search 不属于这两个槽组。
每个 ASGI app / worker 计数独立;多 worker / 多副本会使部署总并发随数量倍增,没有分布式限流或跨进程全局配额。需要部署级上限时须在网关或共享限流层另行控制。
3. 程序格式¶
{
"language": "qatlas-expr-v1",
"filter": "lexical >= 0.5",
"score": "lexical * exp(-coalesce(age_years, 10) / 2)"
}
language、score 必填;filter 可选,默认
true。字段必须是字符串,不接受未知字段或额外配置。
filter 必须返回布尔值,score 必须返回非空有限数值。
先过滤,再评分;false 候选的 score 不执行。
服务对完整的合并候选集评分,不先按默认分数截断;按 score 降序,同分保持合并顺序,最后截取 max_results。
平台不强制词法优先、不限制权重、不强制归一化;但不会替程序补充不存在的特征。
参数冲突返回 400:
ranking=scorer 却缺少 scorer;
提交了非 null scorer,却未选择 ranking=scorer;
ranking=scorer 与 mode=multi 同时使用。
multi 无自定义评分,原有合法 auto/agent 参数继续忽略。explain 默认为 false,必须是真正的 JSON 布尔值;默认/auto 不生成新增解释树。
4. 六个特征¶
特征基于两轮检索汇总、身份去重和元数据合并后的论文,按实际执行路径惰性读取。
特征 |
类型 |
定义 |
|---|---|---|
lexical |
number |
当前未加权词项命中基础分,标题全权重、摘要半权重,0~1 |
phrase |
number |
当前短语奖励,标题每短语0.5、摘要0.25;可累计超过1 |
citations |
number 或 null |
累计引用数,负哨兵或未知为null,已报告的0保留0 |
year |
number 或 null |
发表年份,缺失或非正值为null |
age_years |
number 或 null |
max(本次参考年份-year,0),无年份为null |
source_count |
number |
合并后不同非空来源数量 |
词法/短语特征复用现有实现,不升级分词规则。空文本身份查询的 lexical、phrase 为0;若设置词法门槛导致身份命中被过滤,服务不擅自放宽。
不提供 raw_score/raw_rank、fame、任意字符串运算或跨候选集合统计。引用量不是“著名程度”的唯一含义,缺少的数据不会由公式凭空生成。
5. 语法与类型¶
支持有限十进制数字(含小数/科学计数)、小写 true/false、特征名称、括号以及:
+ - * / 算术及一元正负
< <= > >= == != 单次比较
not and or 布尔逻辑
优先级从高到低:括号/函数、一元正负、乘除、加减、比较、not、and、or。二元算术左结合。禁止链式比较,写成 a < b and b < c。
函数 |
约束 |
|---|---|
min(a,b), max(a,b), abs(x) |
非空有限数值 |
clamp(x,lo,hi) |
lo <= hi |
log1p(x) |
ln(1+x),x > -1 |
sqrt(x) |
x >= 0 |
exp(x) |
结果必须有限,溢出报错 |
coalesce(x,fallback) |
fallback是非空同类值,只在x为空时执行 |
is_missing(x) |
返回是否为null;也允许传非空值 |
where(condition,a,b) |
condition必须布尔;分支基类型一致,只执行选中分支 |
所有函数只接受固定数量的位置参数。and/or 短路执行,布尔不隐式转数字。没有循环、赋值、导入、属性、下标、自定义函数、幂、容器、反射、网络或文件访问。没有字符串、null字面量、注释、Unicode标识符、下划线数字或十六进制数字。
缺失值要显式处理¶
log1p(citations) # 422:citations可能为空
log1p(coalesce(citations, 0)) # 合法:作者明确选择缺失按0处理
编译器不做filter/分支的非空推导:即使filter已经检查引用非空,score中仍要写coalesce。where任一分支可能为空,则整个表达式也可能为空。
6. 结果与解释¶
自定义 score 可以为负或大于1,保留浮点精度,不可当作概率,也不能自动跨程序比较。
响应 ranking 记录:
source=scorer,weights=null;
language、feature_version、scorer_hash;
固定的 as_of_year;
score_scale=custom;
实际生效的 scorer 程序(外侧空白已规范化,filter缺省补true)。
哈希由带语言/特征版本的规范化类型计划生成:空白、冗余括号、等价数字拼写不改变哈希;年份、查询和候选数据不在哈希内。
同程序、同版本、同查询、同合并记录和同参考年份才应得到相同结果。线上引用数、来源或年份变化可以改变分数。
explain=true¶
自定义 hit 的 score_detail=null;不伪造五分量账单。score_explanation来自同一次真实执行,包含参与特征、中间值、分支、缺失替代、候选序号和最终分数。
仅返回top hits的解释;不重新求值以生成解释。
解释列表JSON编码预算64KiB,采用无缩进序列化口径;这是解释数据预算,不是整个响应体预算。
执行器按候选/请求的top预算分配轨迹容量,有界保留top轨迹。
裁剪明确标记truncated;极紧预算时可能仅返回
{"truncated": true},不影响分数。默认/auto的score_detail及四位小数账单保持原样。
7. 错误和资源限制¶
情况 |
HTTP结果 |
|---|---|
缺少/错误服务令牌 |
401,搜索在读取请求体前鉴权 |
模式冲突 |
400 |
非UTF-8搜索JSON或字面NUL字节 |
400 |
搜索请求体超过64KiB |
413,按实际收到的字节数检查,包括分块请求 |
scorer对象超过4KiB |
413 |
JSON容器嵌套超过64 |
422,在JSON完整解析前拒绝 |
程序语法/类型/未知名称/节点深度超限 |
422,在任何数据源和LLM调用前拒绝 |
合并后候选超过1000 |
422,不静默截断 |
实际执行节点除零、定义域错误、非有限结果 |
422,带表达式位置和candidate_index,不返回半套排名 |
全部被合法过滤 |
200,空hits,不自动放宽规则 |
filter+score合计最多256个AST节点,深度最多32(包含表达式根、操作符及名称上下文);解析前另检查括号/容器语法嵌套32。具体计数口径可查询capabilities。
HTTP的scorer大小按所提交字段的紧凑UTF-8 JSON(不ASCII转义、表达式外空白规范化前)计算;CLI文件限制按实际文件字节。候选限额按真实合并集合计算,不能仅因各源预算之和超过1000就推断唯一候选必然超限。
scorer错误返回安全detail对象(code/message/field/可选position/candidate_index),不回显原始对象或配置。位置采用1起始、对应返回的有效表达式,结束位置不包含在范围内。
显式scorer错误不回退默认、不跳过坏候选、不调用后续总结。backend失败仍按原有errors按源报告;scorer和数据源错误是两套不同契约。
程序只在本次请求编译一次,无全局无界编译缓存。执行器解释不可变的白名单计划,不使用eval/exec或Python字节码执行。
8. 离线测试与性能复测¶
从仓库根目录使用锁内开发依赖:
uv sync --locked --extra dev
uv run --no-sync --offline python -m pytest -q
uv run --no-sync --offline python scripts/benchmark_scorers.py --repeats 5
测试使用固定论文和模拟数据源,不需要服务配置或外网。覆盖原有路径、自定义语言攻击输入、 CLI/HTTP结果一致性、短路/缺失、程序与请求预算、解释、身份查询和reinject。
性能脚本使用已经合并的100/1,000个候选,固定参考年份2026及explain_top=100;记录重复运行中位数、 独立编译成本和另一次执行的Python分配峰值。它不测上游网络、去重、HTTP链路或真实检索质量, 也不是延迟SLA。实际运行结果应随环境重新测量。
9. 接入边界¶
/v1/survey-search 与本功能共存,但它不是 scorer 入口。该端点显式拒绝
顶层 ranking/scorer/explain(含null/false),不自动执行或生成评分程序。它与 /v1/search
共享请求体及预鉴权保护,其他规划/过滤/输出仍是独立协议,详见 Survey 整合指南。
默认离线测试覆盖微服务HTTP(进程内TestClient)与CLI direct;不代表真实服务或第三方API已通过集成验证。
接入qatlasd/前端链路前需检查请求透传、ranking.source、自定义负分/大于1的分数和解释字段;发布本服务不自动保证该链路兼容。
default_tools、服务令牌配置与部署网络假设不变;本功能不是对项目已有配置鉴权风险的全面整改。
scorer只能重排已召回候选,不能改变源内召回和二轮白名单。
本服务不提供独立/rank、候选导出或自动优化程序循环。
版本、预发布和失败恢复统一遵循 项目概览:版本与发布开发流程,不在功能指南中单独维护另一套发版步骤。