WIKI 知识库 v1.1.4 正式发布|答案能点回原文了
WIKI 知识库 v1.1.4 正式发布,新增引用归因功能。检索命中分片会按列表顺序编成 [1][2][3],塞进提示词要求模型句末标号,流式答案里解析 [n],前端角标可点击定位第 n 条来源卡。验收口径(产品规划)归因率 ≥95%,跳转正确率抽检 ≥99% 不明显拖慢首 token。
那么这个功能能解决什么问题呢?
混合检索再准用户心里仍会问一句这段话到底从哪来的?以前常见问题有三种:
- 回答写得很自信但侧栏来源对不上没法核对。
- 模型随口写据资料显示却没有可点的角标。
- 有检索命中但答案和来源列表各说各的信任感上不去。
v1.1.4 把链路钉死成一条可核对路径:
检索命中分片
→ 按列表顺序编成 [1][2][3]。
→ 塞进提示词要求模型句末标号。
→ 流式答案里解析 [n]。
→ 前端角标可点击定位第 n 条来源卡。
验收口径(产品规划)归因率 ≥95%,跳转正确率抽检 ≥99% 不明显拖慢首 token。
整体链路方便转发分享
┌──────── 用户检索问答页────────┐
│ 提问 → SSE 流式回答 │
│ 答案中的 [1]/[2] 可点击 │
│ 右侧「知识来源」高亮对应分片 │
└──────────────┬───────────────────┘
│ POST /api/query?stream=1
▼
┌──────── 后端 KnowledgeBaseApi ────┐
│ 1. 检索与证据门槛 │
│ 2. _assign_source_ranks → rank=n │
│ 3. _build_context → n 文件名 │
│ 4. 系统提示词强制须标注来源编号 │
│ 5. SSE meta.results + token 流 │
└────────────────────────────────────┘
| 层次 | 路径 | 说明 |
|---|---|---|
| 上下文编号 | src/api/http_server.py → _build_context |
按顺序生成 [1] 文件… |
| 提示词约束 | _base_system_prompt(with_context=True) |
要求句末标号、禁止编造 |
| 来源名次 | _assign_source_ranks |
rank 与角标同序 |
| 单测 | tests/test_kb12_citation.py |
编号 / 提示词 / 名次对齐 |
| 页面 | frontend/src/views/chat/RetrievalQa.vue |
解析 [n]、点击定位 |
| 流式 API | frontend/src/api/query.ts → streamQuery |
SSE 拿 results + 答案 |
普通用户又怎么用?
- 升级到社区版 v1.1.4。
- 打开控制台 切换至 检索问答。
- 提一个知识库里有依据的问题。
- 看回答气泡句子末尾会出现可点击的
[1]、[2]。 - 点击角标右侧知识来源滚到并高亮对应分片,再点来源卡可核对原文。
若回答是根据当前知识库无法确定:
- 侧栏会改成检索候选避免引用很多条却答不出的误解;
- 候选默认收起需要时可手动展开核对。
带代码开发者怎么接呢?
1. 流式问答一次请求同时拿答案和来源如下示例:
curl -N -X POST "http://127.0.0.1:5000/api/query?stream=1" \
-H "Content-Type: application/json" \
-d "{
\"query\": \"混合检索的 RRF 常数默认是多少?\",
\"k\": 5,
\"generate_answer\": true,
\"deep_think\": false
}"
SSE 里典型会先有一条带 results来源列表,含 rank / filename / preview_text的 meta 再持续推送回答 token。答案文本中应出现与 rank 对齐的 [1] [2],非流式调试方便看整包如下示例:
curl -s -X POST "http://127.0.0.1:5000/api/query" \
-H "Content-Type: application/json" \
-d "{\"query\":\"本地知识库支持哪些导入方式?\",\"k\":5,\"generate_answer\":true}" \
| python -m json.tool
关注字段:
results[].rank来源序号从 1 开始results[].filename/text可核对原文- 回答正文里的
[n]必须落在1…len(results)内
2. 后端:上下文如何编号
核心逻辑简化示意,与仓库实现一致:
# src/api/http_server.py · _build_context
def _build_context(results, max_chars: int = 5000) -> str:
blocks = []
total = 0
for idx, item in enumerate(results, start=1):
filename = str(item.get("filename") or f"doc_{idx}")
text = str(item.get("text") or "").strip()
if not text:
continue
piece = f"[{idx}] {filename}\n{text}"
if blocks and total + len(piece) > max_chars:
break
blocks.append(piece)
total += len(piece)
return "\n\n".join(blocks)
喂给模型的用户侧如下内容:
知识库上下文:
[1] 混合检索说明.md
RRF 默认常数为 60
[2] 检索配置.md
hybrid_enabled 默认 true
用户问题:RRF 常数默认是多少?
系统提示词强制要求:
引用上下文内容时须在对应句子末尾标注来源编号如 [1]、[2],
编号必须与上下文编号一致不得编造不存在的编号。
3. 来源名次角标和侧栏必须同序
# src/api/http_server.py · _assign_source_ranks
@staticmethod
def _assign_source_ranks(items: list[dict]) -> None:
peak = max((float(item["similarity"]) for item in items), default=0.0)
for rank, item in enumerate(items, start=1):
sim = float(item["similarity"])
relative = 1.0 if peak <= 1e-12 else max(0.0, min(1.0, sim / peak))
item["rank"] = rank
item["relative_similarity"] = round(relative, 4)
约定答案里的 [n] = 当前来源列表的第 n 项(1-based)。证据门槛过滤后会重新编号避免角标指向被丢掉的旧序号。
4. 前端把 [n] 变成可点按钮
// frontend/src/views/chat/RetrievalQa.vue · answerSegments(示意)
function answerSegments(msg: ChatMessage) {
const text = String(msg.content || '')
const total = (msg.sources?.length ? msg.sources : sources.value).length
const out = []
const re = /\[(\d+)\]/g
let last = 0
for (let m = re.exec(text); m; m = re.exec(text)) {
const n = Number(m[1])
if (n < 1 || n > total) continue // 越界编号直接忽略,防幻觉角标
if (m.index > last) out.push({ text: text.slice(last, m.index), cite: 0 })
out.push({ text: m[0], cite: n })
last = m.index + m[0].length
}
if (last < text.length) out.push({ text: text.slice(last), cite: 0 })
return out.length ? out : [{ text, cite: 0 }]
}
点击角标时调用 locateSource(msg, n),高亮 sources[n - 1]越界的 [99] 不会变成可点按钮——这是有意的避免假引用。
5. 自测仓库里已有定向用例
python -m pytest tests/test_kb12_citation.py -q
用例会检查:
- 上下文出现
[1] 文件名、[2] 文件名 - 提示词包含标注来源编号,不得编造
rank为完整的1…n答案角标落在合法区间。
怎么和之前版本衔接
| 版本 | 能力 | 和归因的关系 |
|---|---|---|
| v1.1.2 | 检索配置热更新 | 调 k / 阈值,影响「能归因到哪些块」 |
| v1.1.3 | 混合检索 | 召回更稳,归因才有东西可点 |
| v1.1.4 | 引用归因 | 把命中变成可核对的 [n] |
当没有可靠检索归因只是空壳没有归因检索再准也难让人放心,两者是一套质量主轴上的前后环。
总结
v1.1.4 要交付的不是多画几个角标而是一条可复核的信任链路如下链路:
检索 → 编号 → 生成 → 点击 → 原文。
欢迎试用提 Issue 提 PR。