ARTICLE DETAIL

资讯详情

深耕商务建站与企业官网运营的一线实战洞察。

一套代码接入所有大模型:LiteLLM统一接口实战指南

一套代码接入所有大模型:LiteLLM统一接口实战指南 1. 为什么需要一个“中间层”多模型接入的混乱我受够了做AI应用开发的人这两年应该都有一种共同的感觉模型厂商越来越多能力越来越强但接入方式却各自为政。你接完OpenAI的接口下一次要接国产开源模型发现参数名对不上返回结构不一样错误码含义完全不同。辛苦适配完两个产品经理又过来说要对比一下另外几个模型的回答质量你内心已经崩溃了。每个厂商都有自己的SDK、自己的鉴权方式、自己的请求格式甚至同一个模型在不同地区的接入域名还不一样。这套“N个模型 × N套代码”的适配工作量已经远远超过了业务本身的开发量。我第一次接触到LiteLLM就是在这种背景之下。当时手上有个跨平台小项目需要同时调用多个商用API和几个开源的本地模型用来做统一的文本生成和意图识别。一开始我天真地想直接分别封装几个函数不就行了反正也就几个模型嘛。结果真做起来就发现不对。第一个模型返回的字段叫choices第二个叫output第三个干脆把结果藏在data里。错误处理更是五花八门有的超时报错有的限流返回429但格式不一样还有的莫名其妙返回个200但是里面全是空内容。更别提有些模型的上下文窗口限制和计费口径不同成本统计完全要靠自己拿Excel表格手动记。那段时间我每天最忙的不是写业务代码而是在写各种适配器和转换函数。后来同事扔给我一个库说“试试这个”。我一看名字LiteLLM很不起眼。结果测试了一下午感觉整个人都清爽了。它提供了一层统一的中间层带代表接口上面那些乱七八糟的适配全部被收拢成了一套相对标准的请求方式。我当时就在想这东西值得好好写一写。这篇文章不是官方文档的搬运工也不是什么权威指南就是我基于实际项目长期使用LiteLLM之后的经验梳理。里面会包括它核心的设计逻辑、如何在项目里真正落地以及我踩过的那些坑。如果你也想在自己的应用里用一套代码接入多个大模型这篇内容应该能帮你省下不少时间。2. LiteLLM到底做了什么把“所有模型”收拢成“一套接口”2.1 先理解核心思路做一个LLM接入层的“统一封装”LiteLLM的本质是一个Python库附带一个代理服务器Proxy。它做的事情用一句话来总结就是把市面上主流大模型厂商的API请求格式、鉴权方式、返回解析逻辑全部统一成一套接口规范让上层应用代码只管发起标准请求剩下的兼容工作全部交给LiteLLM处理。这句话看起来简单但背后解决的问题非常实际。在应用开发里大模型调用其实分两层一层是“发请求”另一层是“处理响应”。发请求涉及到URL、请求头、鉴权字段、参数命名比如温度参数OpenAI叫temperature有几个模型叫temp甚至top_p的默认值都不一样。处理响应更麻烦不同厂商的返回结构千差万别有的把内容放在choices[0].message.content有的放在generated_text有的还嵌套了一层outputs。你每接一个新模型都要重新分析它的文档写对应的解析逻辑还要处理各种边界情况。LiteLLM把这些全部收拢了。它的completion函数无论背后是OpenAI、Anthropic、Azure还是本地部署的开源模型调用方式都保持一致。你只需要通过model参数指定一个特定的模型标识符比如openai/gpt-4o、anthropic/claude-3-5-sonnet-20241022、ollama/llama3.1LiteLLM会自动识别该去调用哪个提供商的API并按规则转换请求和响应格式。我打个比方。就像你请了一个翻译助理不管来访的客人说的是英语、日语还是德语你只需要用中文把你的需求说一遍。助理负责把话翻译成对方能听懂的语言再把对方的回答翻译回中文告诉你。你不需要学多国语言只需要跟助理一个人沟通就行。2.2 关键设计OpenAI风格语法成了“通用语言”LiteLLM之所以用起来舒服有个很核心的设计选择它选择OpenAI的API风格作为“通用语言”。不管底层是哪个厂商LiteLLM暴露给上层应用的都是类似于OpenAI风格的请求体——一个messages列表里面是{“role”: “user”, “content”: “你好”}这样的结构加上model、temperature、max_tokens这些参数。返回结果也是OpenAI风格的JSON结构choices[0].message.content。这个选择非常高妙。因为OpenAI的接口可以说是眼下大模型行业的“事实标准”绝大多数开发者都对这套结构烂熟于心。而且很多开源模型比如用VLLM或Ollama部署的本身就提供OpenAI兼容接口。LiteLLM直接以OpenAI风格做统一标准意味着学习成本几乎为零。还有一个很实际的便利你写好的那套提示词工程模板、结构化输出的解析逻辑只要调用LiteLLM的接口它们就能在多个模型之间复用不需要针对每个模型单独改一遍。虽然不同模型的提示词格式有差异比如有的开源模型更喜欢|begin_of_text|这种特殊token但这些差异LiteLLM会在内部帮你处理对上层代码完全透明。2.3 两种用法SDK调用和代理网关模式LiteLLM有两种主要的用法我在项目里都试过这里把它们的适用场景交代清楚。第一种是直接在Python代码里用SDK方式调用。你install一个包然后import litellm调用litellm.completion(...)就行。这种模式适合单体应用、脚本工具、快速原型验证只要在代码里配好API Key就行。优点是轻量、直观、不需要额外部署服务缺点是多套代码都需要各自配密钥无法集中管控。第二种是启动LiteLLM代理服务器。它本质上就是一个基于FastAPI构建的独立服务运行起来之后你的应用只需要像调用OpenAI API一样往这个代理的地址发HTTP请求。代理会在内部根据请求里的model参数路由到对应的真实模型服务商并把结果返回。这种模式适合多服务、多团队共享模型资源的场景可以在代理层统一做鉴权、速率限制、成本追踪、日志记录。你不需要在每个服务里都装LiteLLM只需要在服务里配置一个默认的base_url指向代理地址就行。我实际项目里两种模式都用过。快速写脚本验证模型效果的时候用SDK模式一两个文件搞定零额外依赖。到了正经的生产环境多服务都要调模型我还是一律走代理模式。原因很简单代理模式做到了“模型接入与业务代码解耦”——下游业务完全不知道你用的是哪家模型哪天你想从模型A切到模型B只需要改代理配置或请求里的模型名业务代码一个字都不用动。2.4 不止是统一接口路由、重试、预算这些事也顺手解决了如果LiteLLM只是做接口格式统一那它顶多算一个不错的工具库还谈不上“基础设施”。它真正值钱的地方在于在统一接口的下面还附带了生产环境必须的一堆能力。举个例子自动重试与容错。不同模型服务商的API稳定性差异很大有的动不动就限流返回429有的偶尔返回500。LiteLLM允许你配置重试策略对特定状态码自动重试还可以设置最大重试次数和退避策略。我实测下来配置好重试之后模型调用的整体成功率能从95%左右直接拉到99%以上对于生产系统来说这个提升非常可观。再比如模型路由和fallback。你可以在LiteLLM里配置一组“备选路径”——主模型挂了或超时自动切换到备选模型。我在一个模拟项目里配置了“主用国产开源模型超时自动切换商用模型”的策略线上服务的可用性一下子就上来了。后面LiteLLM还支持按权重在多个模型之间负载均衡这个和Nginx那种负载均衡思路是一样的只不过它均衡的对象是模型API。还有成本追踪和预算限制。它可以根据你实际消耗的tokens数量和各模型的计费单价自动统计总花费和每个请求的花费达到阈值还能自动熔断。对我来说这个功能直接解决了一个老大难问题——月底对账。以前要自己拿着账单一个个核对是哪个功能调用了哪个模型花了多少钱现在直接在LiteLLM后台就能看到明细和汇总。3. 从零开始实操把LiteLLM跑起来3.1 安装与第一次调用安装LiteLLM非常简单我一般用pippip install litellm它依赖的包比较多第一次安装可能要等一会儿。安装完成后用一个最简单的例子验证它是否正常工作。我先用OpenAI的模型做测试假设你已经有了API Keyimport litellm response litellm.completion( modelgpt-4o-mini, messages[ {role: user, content: 简述一下LiteLLM的作用} ], ) print(response[choices][0][message][content])如果你之前用过OpenAI的Python SDK你会发现这个代码结构基本一模一样唯一区别是调用入口变成了litellm.completion。这里有一个很容易踩坑的地方首次使用前一定要设置环境变量OPENAI_API_KEY或者直接在代码里litellm.api_key ...。我之前有一回忘记设置结果直接报一个看起来像网络错误的异常排查了半天才发现是鉴权没通过。首次调用如果能正常返回内容说明LiteLLM的基础调用链路已经通了。这时候我们再试试接一个非OpenAI的模型比如Anthropic的Claude当然你需要相应的API Keyimport litellm response litellm.completion( modelanthropic/claude-3-5-sonnet-20241022, messages[ {role: user, content: 介绍一下你是什么模型} ], ) print(response[choices][0][message][content])注意看代码整体的结构没变变的只是model参数的值。LiteLLM看到anthropic/前缀就知道要调用Anthropic的服务并且在内部完成请求格式和响应格式的转换。提示如果你用的是国内的一些模型服务商通常model参数的格式是服务商前缀/模型名。不清楚具体有哪些前缀可选可以运行litellm.provider_list看一下里面列出了所有LiteLLM支持的服务商标识。3.2 配置多种模型把“钥匙”集中管理起来开发阶段直接设置环境变量没问题但在生产环境里多模型、多Key、多人员协作的情况下更合理的方式是把这些Key集中配置到LiteLLM的环境文件或代理配置里而不是散落在各个服务的环境变量中。我自己常用的方式是用一个.env文件OPENAI_API_KEYsk-xxxx ANTHROPIC_API_KEYsk-ant-xxxx AZURE_API_KEYxxxx AZURE_API_BASEhttps://your-resource.openai.azure.com/ AZURE_API_VERSION2024-02-15-preview然后在代码里加载from dotenv import load_dotenv load_dotenv()这样LiteLLM会自动读取对应的环境变量。在SDK模式下它默认会去环境变量里找服务商的Key。如果你用的是代理模式这个.env文件就放在代理服务的运行目录下密钥不会暴露给下游应用。有一点需要特别提醒LiteLLM的环境变量命名规则基本是“服务商大写前缀 _API_KEY”比如GOOGLE_API_KEY、COHERE_API_KEY、MISTRAL_API_KEY。官方的README列表非常详细不过每次接一个新的服务商最好还是先查一下对应的环境变量名不要想当然。我试过接某个国内开源平台环境变量名我猜了个XINYUAN_API_KEY结果一直鉴权失败后来查文档发现它要求的是MOONSHOT_API_KEY。这种事看起来不起眼但真能坑掉你一下午。3.3 代理模式像搭一个“模型网关”一样集中管控SDK模式适合单个项目但如果你跟我一样手上同时维护几个服务都需要调用不同的大模型那我强烈建议你试试LiteLLM代理模式。启动代理服务非常快一行命令就够litellm --model gpt-4o-mini --port 8000默认情况下它会在8000端口起一个FastAPI服务接口路径是/v1/chat/completions完全兼容OpenAI的调用方式。所以下游应用不需要安装任何额外的SDK直接用OpenAI的官方SDK也行用requests裸调也行只需要把base_url指向http://localhost:8000。例如一个使用OpenAI官方SDK的下游应用from openai import OpenAI client OpenAI( api_keydummy-key, # 代理模式下的key是什么其实无所谓 base_urlhttp://localhost:8000 ) response client.chat.completions.create( modelgpt-4o-mini, messages[{role: user, content: 你好}] ) print(response.choices[0].message.content)这种方式的好处太明显了。如果你的服务之前就是按OpenAI接口标准写的切换到LiteLLM代理根本不需要改业务代码只改一个base_url就行。团队其他同事甚至根本不需要知道LiteLLM的存在。对于代理模式有几个我总结出来的使用心得第一代理模式下model参数请务必带上服务商前缀比如openai/gpt-4oanthropic/claude-3-5-sonnet-20241022。因为代理在接收到请求时需要根据前缀判断去调用哪个服务商的API。如果你只写gpt-4oLiteLLM默认会按openai/处理倒是不会报错但如果你打算路由到其他服务商就必须写完整。第二代理可以指定多个模型同时加载通过配置文件启动。这个我们下一节细说。第三代理默认的端口是4000还是8000不同版本有差异。我常用的是显式指定--port参数避免记混。启动之后如果发现端口被占用换一个即可但注意下游服务的base_url要跟着改。3.4 用配置文件管理路由一个config.yaml搞定多模型调度单模型启动代理很简单但真实场景里我们往往要同时暴露多个模型给下游调用。这个需求用配置文件就很好解决。LiteLLM支持通过YAML文件定义代理的路由、模型列表、密钥映射等信息。我随手写一个简单配置模拟项目X里实际用的精简版model_list: - model_name: gpt-4o-mini litellm_params: model: openai/gpt-4o-mini api_key: os.environ/OPENAI_API_KEY - model_name: claude-sonnet litellm_params: model: anthropic/claude-3-5-sonnet-20241022 api_key: os.environ/ANTHROPIC_API_KEY - model_name: llama3-local litellm_params: model: ollama/llama3.1 api_base: http://localhost:11434这里有一个比较灵活的地方model_name是我自定义的对外暴露的模型名而litellm_params.model是LiteLLM真正要去调用的模型标识。举个例子我把内部模型claude-3-5-sonnet-20241022映射成对外名称claude-sonnet。下游业务只需要知道claude-sonnet这个名称哪天我把底层模型换成其他版本只需改代理配置下游无感。这个思路和微服务的“服务发现”很类似把所有模型都注册在一个“服务中心”里消费者不需要关心服务提供方的具体位置和实现。启动的时候指定配置文件litellm --config ./config.yaml --port 8000启动成功后你可以访问http://localhost:8000/model/info查看代理暴露了哪些模型方便调试。在配置中有一个常见的坑api_key: os.environ/XXXX这种写法是LiteLLM自定义的语法意思是“从环境变量里读取对应的值”它不是普通的YAML字符串。所以配置里不要写成api_key: ${OPENAI_API_KEY}——LiteLLM不一定认这种写法最好严格按照官方示例来。我因为按传统习惯写了环境变量模板语法遇到过配置解析失败的情况。4. 核心机制深挖路由、重试、成本治理是怎么协同工作的4.1 fallback机制让“模型挂了”不再是事故我在前面提到LiteLLM支持fallback这里详细展开一下它是怎么配置的以及背后的逻辑。所谓fallback就是你指定一个主用模型再指定一个或多个备选模型。当主用模型调用失败超时、限流、返回异常时LiteLLM会自动转向备选模型继续请求。对上层应用来说这次请求可能只是稍微慢了一点但拿到的结果依然是有效的。在SDK模式下一个简单的fallback示例import litellm response litellm.completion( modelopenai/gpt-4o-mini, messages[{role: user, content: 你好}], fallbacks[anthropic/claude-3-5-sonnet-20241022, ollama/llama3.1] )上面这个配置的意思是优先使用gpt-4o-mini如果失败依次尝试claude-3-5-sonnet-20241022和本地ollama/llama3.1。在代理模式下可以在配置文件的litellm_settings里设置litellm_settings: fallbacks: [{openai/gpt-4o-mini: [anthropic/claude-3-5-sonnet-20241022]}]我实际用下来至少有两个需要注意的点。第一个是fallback的触发条件。默认情况下LiteLLM只有在调用真正抛异常的时候才会触发fallback。但有些厂商的API在服务异常时会返回一个HTTP 200里面藏着错误信息。如果不去解析内容LiteLLM会认为调用成功了自然不会触发fallback。所以我在代理模式里通常会加一层“内容校验”逻辑或者配合健康检查来弥补这个问题。第二个是fallback的耗时。主模型如果是因为超时被触发的那超时时间本身就占了一段再加上备选模型的一次往返整体延迟可能翻倍。对于实时性要求高的场景我建议把超时时间调短一点宁可多触发几次fallback也不要让用户一直等待。这也是为什么我把timeout参数显式设为10秒的原因。4.2 重试策略与幂等性面向失败去设计生产环境中模型API调用失败是常态关键在于你如何处理失败。LiteLLM的重试机制本质上和应用开发里的“指数退避”是一回事。你可以通过环境变量或配置自定义重试次数和退避策略LITELLM_RETRY3 LITELLM_RETRY_BACKOFF2这里LITELLM_RETRY是重试次数LITELLM_RETRY_BACKOFF是基础退避间隔秒。比如第一次失败后等待2秒再重试第二次失败后等待4秒依此类推。有一点一定要搞清楚重试只有在请求是幂等的情况下才安全。什么意思就是“同样的请求重复发几次结果是一样的”。大模型调用本身通常是无状态的你发一个messages列表得到一次生成结果重发一遍照样能得到另一次结果不会产生副作用。所以重试大模型请求一般是安全的。但如果你在业务逻辑里把模型输出直接写入了数据库并且没做去重重试就可能导致重复写入。这种问题不怪LiteLLM而是应用设计时必须考虑的。另外我特别想强调一点LiteLLM默认遇到限流或超时会根据状态码自动重试但它并不会对所有异常都无脑重试。比如遇到鉴权失败401重试多少次都没用它会直接抛错。这个设计是合理的省得浪费请求配额。4.3 预算帽与成本追踪让每一分钱都有迹可循做应用的人大多都有过这种经历模型接口上了生产没人盯着成本结果月底账单出来发现花费远超预期。LiteLLM的内置成本追踪和预算控制就是为了解决这个痛点。在SDK模式下每次调用后你都可以从响应中获取成本信息。如果你在启动代理时开了--detailed_debug或配置了litellm_settings: set_verbose: True默认的回调就会记录详细的花费。此外LiteLLM还支持将成本数据写回Prometheus等可观测平台做实时监控。下面是一个成本追踪示例import litellm litellm.success_callback [datadog] response litellm.completion( modelgpt-4o-mini, messages[{role: user, content: 算一笔账}] ) print(f该请求花费: ${response.get(cost, 0):.4f})更实用的是“预算帽”。你可以在代理配置里设置一个最大预算比如litellm_settings: max_budget: 100 budget_duration: 30d意思是30天内代理累计消耗上限是100美元。一旦达到这个额度LiteLLM会自动拒绝新的模型请求防止成本失控。并且可以设置telemetry回调在预算快用尽时提前发告警通知。我用成本追踪功能帮朋友排查过一个问题。某个服务的模型用量在他不知情的情况下翻了十倍他一度怀疑是代码有bug导致死循环调用。后来通过LiteLLM的日志逐条排查发现是某个定时任务把循环条件写错了每天半夜疯狂调用模型。如果没有详细的调用日志和成本记录这种问题真的很难定位。4.4 结构化输出与工具调用的处理现在不少项目用到大模型不仅仅是闲聊而是让它返回结构化JSON、抽取数据、或者调用外部工具。这块LiteLLM也做了统一。以代码中常用的response_format参数为例import litellm response litellm.completion( modelgpt-4o-mini, messages[{role: user, content: 从这句话中抽取实体: 张三明天去北京}], response_format{type: json_object}, )在底层是OpenAI或兼容OpenAI协议的服务商那边json_object这种响应格式是原生支持的LiteLLM直接把参数透传过去。但如果底层模型服务商不支持这种参数LiteLLM会尝试做兼容处理或者忽略掉不会因为多了个参数就报错。不过这一点不同服务商的表现并不一致所以如果你对结构化输出的稳定性要求很高建议先用真实模型测试一遍再决定是否依赖这个能力。工具调用Function Calling也是同样的道理。LiteLLM把OpenAI风格的tools参数透传给支持工具调用的模型并统一包装返回结果。我之前在一个模拟项目里用它来做“AI助手自动查天气”把天气查询工具注册进去模型根据用户的自然语言自动决定是否调用工具以及传入什么参数。整个过程在LiteLLM的封装下底层模型的差异被隐藏得非常好。5. 我踩过的坑高频故障与排查思路5.1 这些报错们都是在说什么我把自己在实际使用中遇到的高频问题整理成一个速查表方便你遇到类似情况时快速定位。现象可能原因排查方向调用时返回“AuthenticationError”API Key未设置或设置错误检查环境变量是否被正确加载服务商Key是否有效请求超时不返回模型服务商响应慢或本地网络不通尝试直接curl模型服务商API确认服务商本身是否可达返回内容乱码或截断max_tokens设置太小或模型本身不支持中文调大max_tokens换个对中文友好的模型代理启动后访问404请求路径不是/v1/chat/completions确认下游SDK的base_url和请求路径是否拼接正确模型名带前缀才能用model参数没写提供商前缀写完整标识如anthropic/claude而不是只写claudeKey正确但一直429触发了服务商限流检查是否单账号并发过高配置重试和退避报错“Unknown provider”服务商前缀拼写错误或LiteLLM暂未支持用litellm.provider_list命令查所有支持的提供商标识成本统计始终为0缺少对应模型的定价数据更新LiteLLM版本新版通常有新模型定价表5.2 代理模式下的内存与文件句柄问题有一个问题让我印象很深LiteLLM代理在长时间运行后偶尔会出现内存增长、句柄不释放的情况。排查下来的原因基本指向默认日志级别在长时间运行下产生大量日志堆积加上某些回调未及时清理导致的资源占用。虽然是老版本的问题但我建议生产环境的代理服务最好加上进程守护并在负载均衡层做多副本部署单点挂了也不影响整体。LiteLLM代理本身是无状态的除了缓存和埋点之外横向扩展非常容易不必追求单实例一直不重启。还有一个小提醒如果你的下游应用是Python多进程模型使用SDK模式时要注意litellm的异步回调配置。在多进程环境下回调函数如果存在共享状态容易出现计数器错乱或重复上报。稳妥的做法是把可观测的职责放到代理层而不是在每个业务进程里单独开回调。5.3 模型差异造成的提示词“漂移”即使接口被LiteLLM统一了不同模型对同一段提示词的实际反应仍会有明显差异。这不是LiteLLM能解决的而是大模型本身的特性。很多人在做完接口统一后误以为模型行为也统一了这是一个认知误区。我在实际项目中遇到过这种场景同样的提示词在OpenAI上能稳定抽取用户意图换到一个开源模型上就开始胡说。后来我针对不同模型分别维护了一套提示词前缀在调用时根据model参数选择对应的系统提示词版本。LiteLLM提供了自定义system_message的便捷方式我配合模板引擎做了一套多版本管理问题就缓解了。所以现在但凡有人问我“为什么用了LiteLLM还是觉得模型表现飘”我一般都会先反问一句接口统一了但你针对不同模型的提示词策略有没有做适配接口适配只是第一步模型评测和提示词适配是必须同步进行的另一件事。5.4 调试技巧开启Verbose模式观察内部流转LiteLLM把底层封装得很彻底这在生产环境里是优势在开发和排查问题时反而是障碍——你搞不清楚它内部到底走了什么路径、调用了哪个服务商、为什么失败。好在它提供了verbose模式。在SDK模式里import litellm litellm.set_verbose True开启后控制台会打印出每一次请求的完整信息包括实际请求的URL、请求体、响应体、重试次数、耗时等。代理模式可以通过环境变量或配置文件的set_verbose: True实现。这个模式我强烈建议在开发环境开着排查问题效率能提升不少。但也别在生产环境一直开启第一是日志太冗长第二是请求体会包含完整对话内容有数据隐私方面的顾虑。6. 接入LiteLLM后的整体收益一次投入持续回报坦率地讲LiteLLM本身不是什么花哨的技术它做的事情用一个词概括就是“抽象”。但正是这层抽象给项目带来的收益是全方位的。我在那个跨平台项目里最初只是图省事用了LiteLLM后来项目迭代到第三期产品经理提出要快速接入一个新厂商的新模型做对比测试。按照以前的工作方式这个需求起码要花两三天的开发时间包括看文档、写适配、联调。但用了LiteLLM之后新增模型只需要在代理配置里加几行YAML甚至不用改任何业务代码十几分钟就搞定了。负责这个需求的同事当时都不敢相信。对我来说这是LiteLLM最大的价值它不是帮你“写代码”而是帮你节省了未来持续的维护成本。每一次新模型发布、每一次厂商接口调整、每一次从一个服务商迁移到另一个服务商你都会因为当时的这个选择而受益。这种收益很难量化但做技术的人都明白它比单纯减少几行代码有意义得多。如果你正在做一个长期迭代的AI应用或者维护着多个都要调用大模型的服务我建议你抽半天时间把LiteLLM跑通一次。从最简单的SDK调用开始再到代理模式感受一下“一套代码接所有模型”的体验再按上面我讲的那些经验逐步加深配置。等你把重试、fallback、成本追踪这些能力都加上之后你会发现自己真的从“模型API适配工”变成了真正在做业务开发的工程师。
返回列表
PREV
查看更多资讯
NEXT
返回资讯列表