让LLM在长上下文问答中生成细粒度引用的简介、安装和使用方法、案例应用之详细攻略)
1. 长文档问答为什么需要 LongCite从幻觉到句子级引用如果你做过长文档问答大概率遇到过这种场景把一份 80 页的 PDF 丢给模型问它“这份合同里关于违约责任的条款是怎么约定的”模型洋洋洒洒写了一大段看起来头头是道但你拿着答案去原文里核对翻了三遍都找不到对应出处。更糟的是有些细节模型说得斩钉截铁原文里压根没提——这就是长上下文 RAG 里最让人头疼的幻觉问题。LongCite 想解决的就是这件事。它是清华和智谱团队在 2024 年 9 月开源的一套方案核心目标是让长上下文 LLM 在回答问题时能生成句子级的细粒度引用。什么意思就是模型每说一句事实性陈述后面都跟着一个引用标记指向原文里具体支持这句话的句子编号。你拿到答案后不用再满篇找证据直接看引用编号就能定位到原文第几句。它包含两个底座模型基于 GLM-4-9B 微调的 LongCite-glm4-9b以及基于 Llama3.1-8B 微调的 LongCite-llama3.1-8b都支持最大 128K 上下文。配套还有 LongCite-45k 数据集44600 条带句子级引用的长上下文问答实例、CoF 数据构建管道以及 LongBench-Cite 评测基准。这套东西适合谁我梳理了三类一是做企业知识库问答的开发者需要答案可溯源二是研究 RAG 引用生成方向的同学想复现论文效果三是手里有长文档处理需求、又不想自己从零训模型的工程团队。下面我从环境准备一路写到引用质量验证把踩过的坑都标出来。2. TaoToken 前置准备给 LongCite 推理链路配一个稳定入口LongCite 本身是本地加载模型推理但实际用起来你会发现两个绕不开的环节一是 CoF 数据构建管道里要调用 LLM API 来生成 QA 对和引用二是 LongBench-Cite 评测时用 GPT-4o 当评审也要走 API。这两个环节如果 API 入口不稳定整个流程会卡在半路。我自己的做法是把模型调用统一走 TaoToken 的 API 入口这样 CoF 管道和评测脚本里的llm_api.py配置一次就行。TaoToken 的 API 地址是https://taotoken.net/api兼容 OpenAI 的调用格式所以 CoF 里那些基于 OpenAI SDK 写的脚本基本不用改代码只换 base_url 和 key。具体操作分三步。第一步去控制台创建 API Key地址是https://taotoken.net/console/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi_keysutm_campaignrewrite创建后复制保存后面配置里要用。第二步如果你只是想先验证模型对话效果可以直接在模型对话页面试地址https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_contentmodel_chatutm_campaignrewrite选一个长上下文模型丢一段文档进去问感受一下带引用的输出长什么样。第三步如果你打算长期跑 CoF 管道或者做 Agent 类实验建议看下 Coding Plan地址https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding_planutm_campaignrewrite它更适合高频调用场景。这里要强调一点TaoToken 在这里的角色是模型调用的统一入口不是替代你本地的 LongCite 模型推理。LongCite-glm4-9b 和 LongCite-llama3.1-8b 还是在你本地用 transformers 或 vllm 加载TaoToken 负责的是 CoF 管道和评测环节里那些需要调用外部 LLM 的步骤。两者分工明确别搞混了。配置的时候有个细节CoF 目录下的utils/llm_api.py里通常写死了 OpenAI 的 base_url你要把它改成 TaoToken 的地址。同时环境变量里设置OPENAI_API_KEY为你刚创建的 key。这样 CoF 的四个脚本跑起来才会走 TaoToken 的通道。如果你用的是 Cline 或者 Claude Code 这类工具来辅助调试记得把 Base URL、Key、Model ID 三件套都填全缺一个都会报连接错误。3. 可复制配置LongCite 环境搭建与 CoF 管道参数这一节给你可以直接抄的配置片段。先说 LongCite 模型推理的环境。官方建议transformers4.43.0我实测下来 4.43 到 4.45 之间比较稳再高版本偶尔会遇到trust_remote_code加载时的兼容问题。Python 建议 3.10 或 3.11torch 用 2.3 以上配 bfloat16。先建环境conda create -n longcite python3.11 -y conda activate longcite pip install torch2.3.1 torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121 pip install transformers4.44.0 accelerate sentencepiece streamlit vllm然后是最小推理脚本这段可以直接存成run_longcite.pyimport json import torch from transformers import AutoTokenizer, AutoModelForCausalLM model_path THUDM/LongCite-glm4-9b tokenizer AutoTokenizer.from_pretrained(model_path, trust_remote_codeTrue) model AutoModelForCausalLM.from_pretrained( model_path, torch_dtypetorch.bfloat16, trust_remote_codeTrue, device_mapauto ) context W. Russell Todd, 94, United States Army general (b. 1928). February 13. Tim Aymar, 59, heavy metal singer (Pharaoh) (b. 1963). Marshall Eddie Conway, 76, Black Panther Party leader (b. 1946). Robert Geddes, 99, architect, dean of the Princeton University School of Architecture (1965-1982) (b. 1923). Tom Luddy, 79, film producer (Barfly, The Secret Garden), co-founder of the Telluride Film Festival (b. 1943). query What was Robert Geddes profession? result model.query_longcite( context, query, tokenizertokenizer, max_input_length128000, max_new_tokens1024 ) print(Answer:\n{}\n.format(result[answer])) print(Statement with citations:\n{}\n.format( json.dumps(result[statements_with_citations], indent2, ensure_asciiFalse))) print(Context (divided into sentences):\n{}\n.format(result[splited_context]))接下来是 CoF 管道的配置。CoF 目录下utils/llm_api.py需要改 base_url。如果你走 TaoToken配置大概长这样# utils/llm_api.py 关键片段 import os from openai import OpenAI client OpenAI( api_keyos.environ.get(OPENAI_API_KEY), base_urlhttps://taotoken.net/api ) def call_llm(messages, modelgpt-4o, temperature0.7): resp client.chat.completions.create( modelmodel, messagesmessages, temperaturetemperature ) return resp.choices[0].message.content环境变量这样设export OPENAI_API_KEY你的TaoToken Key export OPENAI_BASE_URLhttps://taotoken.net/apiCoF 四个脚本按顺序跑1_qa_generation.py生成 QA 对2_chunk_level_citation.py加粗粒度引用3_sentence_level_citation.py提取句子级引用4_postprocess_and_filter.py过滤低质量数据。每个脚本的输入输出路径在脚本头部有注释改一下input_file和output_file就行。如果你要用 vllm 加速推理官方提供了vllm_inference.py。启动命令类似CUDA_VISIBLE_DEVICES0,1 vllm serve THUDM/LongCite-glm4-9b \ --trust-remote-code \ --max-model-len 128000 \ --dtype bfloat16 \ --tensor-parallel-size 2想跑官方 demo 聊天界面的话CUDA_VISIBLE_DEVICES0 streamlit run demo.py --server.fileWatcherType none这里提醒一句128K 上下文对显存要求不低glm4-9b 在 bfloat16 下光权重就约 18GB加上 KV cache单卡 24GB 跑满 128K 会紧张。实测 32K 上下文单卡 24GB 比较舒服要跑满 128K 建议双卡或者用 vllm 的 tensor parallel。4. 验证请求与成功结果引用质量怎么判断配置跑通后怎么确认 LongCite 真的在生成有效引用而不是随便标几个编号糊弄你我总结了三个验证动作。第一个动作看输出结构。调用query_longcite后返回的 dict 里有三个关键字段answer是纯文本答案statements_with_citations是带引用的陈述列表splited_context是切分后的句子列表。成功的输出里statements_with_citations应该是一个列表每个元素包含statement和citationscitations 里是句子编号区间比如[3-4]表示第 3 到第 4 句支持这个陈述。第二个动作人工核对引用。拿上面 Robert Geddes 的例子模型应该回答他是 architect建筑师引用指向包含 Robert Geddes, 99, architect 的那句。你数一下splited_context里这句的编号和 citations 里的编号对一下能对上就说明引用是准的。我试过几次glm4-9b 版本在英文短上下文上引用准确率很高基本不会指错句。第三个动作跑 LongBench-Cite 评测。这是官方给的自动化基准能算引用召回率和精确度。流程是先跑pred_sft.py让 LongCite 模型生成带引用的回答再跑pred_one_shot.py让普通模型比如 GPT-4o生成对照然后eval_cite.py评引用质量eval_correct.py评答案正确性。评测用 GPT-4o 当评审所以utils/llm_api.py里的 key 要配好。cd LongBench-Cite python pred_sft.py --model_path THUDM/LongCite-glm4-9b --output pred_longcite.jsonl python pred_one_shot.py --model gpt-4o --output pred_gpt4o.jsonl python eval_cite.py --input pred_longcite.jsonl --output eval_cite_longcite.json python eval_correct.py --input pred_longcite.jsonl --output eval_correct_longcite.json评测结果里重点看两个指标citation recall引用召回率衡量该引用的陈述有没有引用和 citation precision引用精确度衡量引用的句子是否真的支持陈述。论文里 LongCite-9B 在这两个指标上超过了 GPT-4o我实测下来在中文长文档上确实比直接让 GPT-4o 裸答要稳尤其是引用粒度明显更细。一个成功的结果长这样答案里每个事实句后面跟着[12-13]这样的标记statements_with_citations里能清楚看到哪句引了哪几句splited_context里对应编号的句子确实是支持该陈述的原文。如果 citations 大量为空或者指向明显不相关的句子那就要检查模型加载是否正确、上下文切分是否符合预期。5. 本篇常见错排查401、local proxy failed 与 reading choices跑 LongCite 和 CoF 管道时我踩过的报错集中在几个地方逐个说。401 错误。这个最常见出现在 CoF 脚本调用 LLM API 时。报错信息通常是Error code: 401 - {error: {message: Invalid API key}}。原因就两个要么OPENAI_API_KEY环境变量没设要么设了但 key 复制时带了空格。检查方法echo $OPENAI_API_KEY看有没有值然后确认llm_api.py里 base_url 是https://taotoken.net/api而不是别的。如果用的是 Cline 或 Claude Code 这类工具去设置里把 Base URL、Key、Model ID 三件套重新填一遍Model ID 要填你实际要调用的模型名别留空。local proxy failed。这个报错一般出现在网络请求环节提示Connection error或local proxy failed。先确认你的 base_url 写对了TaoToken 的 API 地址是https://taotoken.net/api注意结尾不要多加/v1或者斜杠有些 SDK 会自动拼路径多写了会 404。然后确认环境变量OPENAI_BASE_URL和代码里的 base_url 一致别一个走环境变量一个写死。如果还是不通用 curl 测一下curl https://taotoken.net/api/models \ -H Authorization: Bearer $OPENAI_API_KEY能返回模型列表就说明通道没问题问题在代码配置。reading choices 报错。这个通常长这样KeyError: choices或者AttributeError: NoneType object has no attribute choices。原因是 API 返回结构和你代码里取值的路径不匹配。CoF 的llm_api.py里如果用的是老版 OpenAI SDK 的写法resp[choices][0][message][content]而新版 SDK 返回的是对象要用resp.choices[0].message.content。检查一下 openai 库版本pip show openai1.0 以上用对象属性访问0.28 以下用字典访问。统一升级到 1.0 以上然后按对象方式取值。OAuth 相关报错。如果你用 Claude Code 或者某些 CLI 工具接入可能会遇到 OAuth token 过期或者认证失败的提示。这类工具一般有自己的认证流程去工具的配置目录里重新走一遍登录或者检查 token 文件是否过期。如果是通过 API Key 方式接入确认 key 没有过期去控制台重新生成一个。模型加载报错。trust_remote_codeTrue相关的报错通常是 transformers 版本不匹配。降到 4.44.0 试试。还有CUDA out of memory这个就是显存不够减小max_input_length或者用 vllm 的 tensor parallel 分摊到多卡。引用为空。模型输出里statements_with_citations全是空 citations说明模型没学会引用格式。检查你是不是加载了正确的 LongCite 模型而不是基座模型。THUDM/LongCite-glm4-9b和THUDM/glm-4-9b是两个不同的 repo加载错了就不会生成引用。6. 语义一致 CTA把 LongCite 接进你的长文档问答链路LongCite 的价值在于让长文档问答的答案可溯源而实际落地时你往往需要把它和现有的 RAG 链路拼起来。我的建议是分两步走先用 TaoToken 的模型对话快速验证你的长文档场景下带引用输出是否符合预期地址https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_contentmodel_chatutm_campaignrewrite丢一份你的真实文档进去问几个问题看引用粒度够不够细。确认方向对了再本地部署 LongCite 模型做深度集成。集成的时候CoF 管道那套数据构建思路其实可以复用到你自己的数据上。你手里如果有大量长文档和对应的问答对可以照着 CoF 的四步流程用 TaoToken 的 API 跑一遍粗粒度引用生成和句子级引用提取产出带引用的训练数据再微调你自己的模型。这条路径对做垂直领域知识库的团队特别有用。如果你是要长期跑这类长上下文任务或者想搭一个带引用能力的 AgentCoding Plan 会比按次调用更划算地址https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding_planutm_campaignrewrite。接入文档在https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite里面有各语言 SDK 的调用示例照着改 base_url 就行。最后说个实操细节LongCite 的引用格式是statement.../statementcite[s-e]/cite你在做前端展示时可以用正则把 cite 标签解析出来渲染成可点击的角标点一下跳转到原文对应句子。这样用户验证答案的成本就从“翻遍全文”降到“点一下”这才是细粒度引用真正的体验价值。