codecamp

Grok 中文文档问答 RAG 组合

主线项目走到这里要收口了:前面几节分别讲了文件、集合、引用与工具,这一节把它们拼成一条能跑的中文文档问答链路。拼的时候会暴露一个很具体的问题——中文切片按字数切,语义断在半句上;检索只靠向量,专有名词又常常召回不到。

这一节给一条最小管线:切片、混合检索、带引用生成。

一、问题不是「有没有 RAG」,而是「中文这一层怎么处理」

把英文 RAG 的做法直接搬到中文,通常会遇到三处水土不服:

  • 切片:按固定字符数切,中文一句没说完就被截断,向量表示的是半句语义。
  • 检索:纯向量对同义表达友好,但对型号、编号、专有名词不敏感。
  • 溯源:答案看起来有出处,但引用指向的片段并不支持这句话。

三处都不是模型能力问题,而是管线设计问题。所以这一节的重点不在模型,在切法与合并排序。

二、最小可运行示例:混合检索管线

下面的脚本不联网、不需要真实密钥。它用固定的三段落模拟切片结果,把「关键词命中」与「模拟向量分」加权合并,取前两条。

"""最小 RAG 管线(mock):关键词 + 模拟向量,加权合并排序。"""

CHUNKS = [
    {"id": "c1", "text": "缓存从 messages 开头匹配前缀。"},
    {"id": "c2", "text": "200k 阈值会让整请求按高档计费。"},
    {"id": "c3", "text": "推理强度分四档,high 是默认值。"},
]

def hybrid(query: str, chunks: list[dict], k: int = 2) -> list[tuple[float, str, str]]:
    """关键词分与模拟向量分加权;真实实现把 vec 换成嵌入相似度。"""
    q = set(query)
    ranked = []
    for c in chunks:
        kw = len(q & set(c["text"])) / max(len(q), 1)
        vec = 1.0 if any(w in c["text"] for w in ("缓存", "阈值")) else 0.2
        ranked.append((round(0.6 * kw + 0.4 * vec, 2), c["id"], c["text"]))
    ranked.sort(key=lambda x: x[0], reverse=True)
    return ranked[:k]

def offline_check() -> None:
    for i, (score, cid, text) in enumerate(hybrid("缓存阈值", CHUNKS), 1):
        print(f"{i} {score:.2f} {cid} {text}")

if __name__ == "__main__":
    offline_check()

真正上线时,把 CHUNKS 换成切片结果、把 vec 换成嵌入相似度、把 query 换成用户问题;hybrid() 的合并逻辑不用改。关键词分与向量分各占多少权重,要用你的评测集调,不要照抄本节的比例。

三、三个环节的中文处理

环节 中文场景的坑 建议做法
切片 按字数切,句子断在半句 按标题层级与段落切,超长再按句号
检索 纯向量漏专有名词 关键词 + 向量混合,各留一票
生成 答案与引用片段对不上 让模型只依据检索片段,带引用输出

三件要强调的事:

  • 切片要保留结构。中文文档的「第几章、第几节」本身就是语义边界;按标题切,比按 500 字切更不容易断义。切完给每片带上标题路径,检索命中时模型才知道它在哪一章。
  • 混合检索不是二选一。关键词负责「型号、编号、专有名词」,向量负责「同义表达、口语化提问」;两者加权合并,比单独用任何一种都稳。
  • 生成要能回到片段。让答案带引用(见 8.3),并在提示里明确「只依据给定片段回答,片段里没有就说没有」。这句话比任何后处理都更能减少编造。

四、预期输出与异常分支

运行管线,预期输出:

1 0.70 c1 缓存从 messages 开头匹配前缀。
2 0.70 c2 200k 阈值会让整请求按高档计费。

换成真实数据时,若出现异常,按下面顺序定位:

现象 优先怀疑 第一步动作
答非所问 切片断了语义 打印命中的片段,看是否半句
专有名词搜不到 只有向量检索 加关键词通道
答案没出处 提示未要求引用 在提示里要求带来源
引用不支持结论 生成越过了片段 限制「只依据片段」并校验

最后一行尤其重要:RAG 的失败很少是「检索不到」,更多是「检索到了但模型没用它」。所以引用校验和提示约束要一起上。

五、判据卡

判断项 该这么做 不该这么做
切片 按标题与段落切 按固定字数硬切
检索 关键词 + 向量混合 只依赖一种
权重 用评测集调 照抄别人的比例
生成 只依据片段 + 带引用 允许自由发挥
失败处理 片段没有就说没有 编一个看似合理的答案

这张卡的重点是中文这一层要单独设计。五条里有三条在说:切法、召回、溯源,每一环都不能直接套英文方案。

六、来源与核验

  • 官方 Collections Search:https://docs.x.ai/developers/tools/collections-search(核验日期 2026-11-04)。collections_search(xAI SDK)与 file_search(OpenAI Responses API)的对应关系、max_num_results / limit 参数、agentic 多次检索的行为,均以该页为准。
  • 官方 Collections:https://docs.x.ai/developers/files/collections(核验日期 2026-11-04)。嵌入索引、元数据字段与单文件上限来自该页。
  • 官方 Citations:https://docs.x.ai/developers/tools/citations(核验日期 2026-11-04)。citations 与 annotations 的语义来自该页,引用校验见 8.3。
  • 本节为第 8 章的原创综合;切片策略、混合检索权重与提示约束为工程实践,不引用任何第三方的具体指标或代码。中文 RAG 的做法因语料而异,请以自己的评测结果为准。

七、动手试

  1. 拿你的一份中文文档,分别按「500 字」与「标题 + 段落」切一次,各取 5 个片段,比较哪一组更像完整语义。
  2. 给 hybrid() 加一个 kw_weight 参数,在 0.3 / 0.5 / 0.7 三档下各跑一遍同一个问题,记录排序变化,并说明你会选哪一档。
  3. 设计一条「片段里没有答案」的回复模板:既要明确说没有,又不能显得系统坏了。写出你会用的两句话。
Grok 引用核对与答案溯源
Grok 图像理解接入
温馨提示
下载编程狮App,免费阅读超1000+编程语言教程
取消
确定
目录

关闭

MIP.setData({ 'pageTheme' : getCookie('pageTheme') || {'day':true, 'night':false}, 'pageFontSize' : getCookie('pageFontSize') || 20 }); MIP.watch('pageTheme', function(newValue){ setCookie('pageTheme', JSON.stringify(newValue)) }); MIP.watch('pageFontSize', function(newValue){ setCookie('pageFontSize', newValue) }); function setCookie(name, value){ var days = 1; var exp = new Date(); exp.setTime(exp.getTime() + days*24*60*60*1000); document.cookie = name + '=' + value + ';expires=' + exp.toUTCString(); } function getCookie(name){ var reg = new RegExp('(^| )' + name + '=([^;]*)(;|$)'); return document.cookie.match(reg) ? JSON.parse(document.cookie.match(reg)[2]) : null; }