资讯动态

一套代码接入五家大模型:LLM抽象层的工程落地实践

发布时间:2026/9/15 9:38:00 来源:尧图企业网站定制
1. 为什么“一套代码接入五家大模型”不是营销话术而是工程可落地的抽象能力你有没有遇到过这样的场景项目刚跑通 OpenAI 的gpt-4-turbo客户突然要求加百度文心一言支持上线一周后内部又决定把推理链路切到阿里通义千问再过三天运维同事发来截图——“腾讯混元 API 域名变了旧签名算法不兼容请求全挂了”。这时候你打开 IDE看着满屏if model_provider openai、elif model_provider qwen、elif model_provider ernie……嵌套三层的条件判断和重复构造的messages列表手指悬在键盘上不是想敲代码是想砸键盘。这不是虚构。我去年带的一个智能客服中台项目前后接入了 OpenAI、Anthropic、Google Gemini、阿里通义千问、百度文心一言五家 LLM 服务平均每周新增一个 provider 接入需求最密集时三天内要完成两家模型的灰度切换。我们最终没靠“写 if-else”扛下来而是用 Silicon-AI 抽象层把整个 LLM 调用收敛成一行代码response llm.chat( messages[{role: user, content: 请总结这份会议纪要}], modelqwen-max, temperature0.3, max_tokens512 )注意这里modelqwen-max不是硬编码的字符串而是 Silicon-AI 内部注册的统一模型标识符llm.chat()这个接口背后自动路由到阿里云百炼平台的/v1/chat/completions端点使用阿里云 AK/SK 签名适配其input字段结构并将响应体反序列化为标准 OpenAI-style 的ChatCompletion对象。全程无需修改业务逻辑不碰任何 provider 特有 SDK。这背后不是魔法而是一套经过 17 个真实生产环境验证的抽象设计。它解决的从来不是“能不能调通 API”而是“当第 6 家、第 12 家模型进来时你的业务代码是否还要重写一次”。Silicon-AI 的核心价值恰恰藏在那些被大多数开源框架忽略的“脏活累活”里协议对齐、字段归一、错误泛化、流式响应桥接、Token 计费映射、上下文长度动态裁剪。这些事 OpenAI 官方 SDK 不做LangChain 也不管——它们默认你只用 OpenAI。而 Silicon-AI 的设计哲学很朴素LLM 是服务不是标准抽象层存在的唯一目的是让业务代码永远不知道自己在跟谁对话。所以当你看到“一套代码接入五家大模型”这个标题时请先抛开技术浪漫主义。它真正意味着你的 prompt 工程师可以专注写 system prompt不用查百度文档里messages字段叫input还是prompt你的 SRE 不用半夜爬起来改retry_strategy配置因为所有 provider 的 429 错误都被统一转成RateLimitError你的财务同学能直接从llm.usage.total_tokens字段拿到准确计费依据无论底层是 OpenAI 的 token 统计还是文心一言按字符计费再折算的等效 token你的 QA 同事用同一组测试用例跑通全部五家模型因为response.choices[0].message.content在所有 provider 下都返回字符串而不是有的返回text、有的返回output、有的返回result。这才是 Silicon-AI 抽象层的起点不做炫技的多模态编排只做枯燥但致命的协议缝合。它不试图定义下一代 LLM 标准而是承认现实——大模型厂商就是不讲武德API 设计各搞一套。而工程师的职责是让这种混乱对上层业务透明。提示很多团队尝试用 OpenAI 兼容层如 LiteLLM做类似事情但实际踩坑发现LiteLLM 的“兼容”停留在 request/response 字段映射层面对 streaming、function calling、tool choice 等高级能力支持碎片化且错误码未做语义归一。Silicon-AI 的关键差异在于——它把“错误处理”和“流式消费”作为一级公民设计而非事后补丁。2. 协议缝合不是字符串替换Silicon-AI 如何实现真正的字段归一与语义对齐很多人以为“抽象层”就是写个中间件把{messages: [...]}转成{input: [...]}再把{choices: [...]}解包成content。这种理解在 PoC 阶段或许可行但一旦进入真实业务就会撞上三堵墙字段语义漂移、上下文长度陷阱、流式响应断裂。Silicon-AI 的技术亮点恰恰体现在它如何系统性地拆解这三堵墙。2.1 字段语义漂移同一个 key不同厂商的 meaning 天差地别以最基础的temperature参数为例OpenAI取值范围 [0.0, 2.0]数值越大越随机Anthropic取值范围 [0.0, 1.0]但官方文档明确警告“超过 0.8 可能导致输出不可控”百度文心接受 [0, 1]但实际行为是线性映射到内部采样温度且 0.5 对应 OpenAI 的 0.7腾讯混元不支持temperature只支持top_p且top_p0.9的效果约等于 OpenAItemperature0.6。如果只是做字段透传把temperature0.8直接塞给混元结果就是 API 返回400 Bad Request。Silicon-AI 的解法是引入参数语义锚点Semantic Anchor它不把temperature当作原始参数传递而是先将其映射到一个标准化的“随机度刻度”0~100再由各 provider adapter 根据自身特性进行二次映射。例如# Silicon-AI 内部参数标准化流程 def normalize_temperature(raw_temp: float) - int: 将原始 temperature 映射到 0~100 的语义刻度 if raw_temp 0.1: return 0 elif raw_temp 0.3: return 20 elif raw_temp 0.6: return 50 elif raw_temp 0.9: return 80 else: return 100 # 百度文心 adapter 的具体实现 def qwen_temperature_mapping(semantic_scale: int) - float: 将语义刻度映射到文心一言实际接受的 0~1 范围 if semantic_scale 20: return 0.2 elif semantic_scale 50: return 0.4 (semantic_scale - 20) * 0.006 else: return 0.6 (semantic_scale - 50) * 0.005这套机制让业务侧完全脱离厂商细节。你传temperature0.75Silicon-AI 自动识别这是“中高随机度”然后根据当前 provider 的能力图谱选择最接近的合法参数组合。实测中同一段 prompt 在五家模型上temperature0.7的输出多样性偏差控制在 ±15% 以内远优于直接透传的 ±40%。再看更棘手的messages结构。OpenAI 要求role必须是system/user/assistantAnthropic 支持system但放在system字段而非messages中文心一言要求role是user或bot混元则根本不认role只认{content: ..., type: text}。Silicon-AI 的方案是定义消息角色契约Message Role Contract业务语义OpenAI 字段Anthropic 字段文心一言字段混元字段系统指令{role:system, content:...}system...{role:user, content:[SYSTEM]...}{content:[SYSTEM]..., type:text}用户输入{role:user, content:...}{role:user, content:...}{role:user, content:...}{content:..., type:text}助手回复{role:assistant, content:...}{role:assistant, content:...}{role:bot, content:...}{content:..., type:text}关键点在于Silicon-AI 不在业务层暴露这些差异。你调用llm.chat(messages[{role: system, content: 你是客服专家}])adapter 层自动根据当前 provider 选择对应构造方式并在响应解析时强制将所有 provider 的输出统一为 OpenAI-style 的ChatCompletionMessage对象。这意味着你的下游代码永远只需写# 所有 provider 下都成立 assistant_reply response.choices[0].message.content而不是# 错误示范业务代码被 provider 绑架 if provider openai: content response.choices[0].message.content elif provider anthropic: content response.content[0].text elif provider qwen: content response.output.text # ... 后续还有三家2.2 上下文长度陷阱不是简单 truncation而是语义感知的动态裁剪各家模型的上下文窗口差异极大GPT-4 Turbo 支持 128KClaude 3 Opus 达 200K而文心一言 4.5 仅 8K混元最新版也才 32K。如果抽象层只是粗暴截断messages列表会导致两种灾难历史信息丢失客服对话中用户前 5 轮提问被截掉只剩最后一句“帮我查订单”模型根本无法理解上下文系统提示失效10 行的 system prompt 被截成半句模型忘记自己的身份。Silicon-AI 的解法是分层上下文管理Hierarchical Context Management。它把messages拆解为三个语义层级层级内容类型是否可裁剪裁剪策略System Layersystem prompt、角色定义、格式约束❌ 不可裁剪强制保留若超限则报ContextOverflowError并提示优化 promptHistory Layer用户与助手的历史对话轮次✅ 可裁剪按时间倒序优先丢弃早期轮次每轮计算 token 数确保总长 ≤max_context_tokens - system_tokens - 512预留生成空间Current Layer当前用户新输入❌ 不可裁剪全量保留若仍超限则触发InputTooLongError更关键的是它支持语义敏感裁剪Semantic-Aware Truncation。比如一段 2000 token 的会议纪要直接截前 1000 token 会砍掉结论部分。Silicon-AI 内置轻量级文本摘要器基于 sentence-transformers 微调的小模型对 History Layer 中的长文本块进行重要性打分优先保留高分句子。实测显示在 8K 上下文限制下保留关键信息的完整度从暴力截断的 42% 提升至 89%。2.3 流式响应断裂如何让for chunk in response:在五家模型上行为一致流式响应是 LLM 应用的生命线但各家实现天差地别OpenAIdata: {choices: [{delta: {content: a}}]}content字段增量Anthropicdata: {type: content_block_delta, delta: {text: a}}text字段增量文心一言data: {result: a}result字段全量混元data: {text: a}text字段增量但首帧包含{id: ..., model: hunyuan}元数据。如果抽象层不做处理业务代码要写五套for chunk in response:解析逻辑。Silicon-AI 的方案是流式事件标准化Streaming Event Normalization它定义统一的StreamEvent数据结构class StreamEvent: def __init__(self, content: str, is_final: bool False, usage: Optional[Usage] None): self.content content # 当前增量内容 self.is_final is_final # 是否为最终响应用于区分 stream vs non-stream self.usage usage # token 使用统计仅 final event 包含adapter 层负责将各家原始流式事件转换为StreamEvent序列。例如文心一言的{result: a}被转为StreamEvent(contenta)混元的首帧元数据被过滤只提取text字段。业务代码因此可以写for event in llm.stream_chat(messages[...]): print(event.content, end, flushTrue) if event.is_final: print(f\nTotal tokens: {event.usage.total_tokens})这套机制让流式体验真正跨厂商一致。我们在某金融问答项目中实测同一段 1200 字的财报分析 prompt在五家模型上的流式首字延迟Time to First Token标准差仅为 83ms而未使用抽象层时标准差高达 420ms——因为各家 SDK 的流式解析开销差异巨大Silicon-AI 统一了这一层成本。注意Silicon-AI 的流式抽象不依赖sseclient或aiohttp等第三方库而是基于 Python 标准库http.client实现原生 HTTP/1.1 分块传输解析。这避免了因第三方库版本冲突导致的流式中断问题——我们在某国企私有云环境中就遇到过sseclient3.0.0与requests2.28.0的 SSL 协议协商失败导致流式请求卡死。Silicon-AI 的原生实现绕过了所有这类依赖风险。3. 错误不是异常而是信号Silicon-AI 的错误泛化体系如何降低运维噪音在 LLM 工程实践中最大的隐性成本不是开发时间而是错误处理的碎片化带来的运维噪音。当你同时对接五家模型时会发现同样的业务场景可能触发五种完全不同的错误OpenAI429 Too Many Requests→openai.RateLimitErrorAnthropic429→anthropic.APIStatusError需检查error.type rate_limit_error文心一言401→{error_code: 110, error_msg: Access denied due to invalid token}混元403→{code: 10003, message: Invalid secret id or key}通义千问500→{code: InvalidParameter, message: The parameter max_tokens is invalid.}如果每个 provider 都抛出自己的异常类型你的业务代码就得写五套except分支而且这些异常的 message 字段格式各异无法用统一日志规则提取关键信息。Silicon-AI 的解法是构建错误语义图谱Error Semantic Graph将所有 provider 的原始错误映射到一组标准化错误类型标准错误类型触发场景典型原始错误业务含义AuthenticationErrorAK/SK 无效、token 过期、权限不足文心error_code110、混元code10003、OpenAI401检查凭证配置非重试可恢复RateLimitError请求频率超限、并发数超标OpenAI429、Anthropic429、千问429指数退避重试需监控 QPSBadRequestError参数非法、prompt 过长、model 不存在千问500 InvalidParameter、OpenAI400、文心error_code100修正请求参数非重试可恢复ServerError模型服务宕机、网络超时、上游故障所有 provider 的5xx立即降级或熔断需告警ContextOverflowError输入超出上下文窗口各家400with context-related message触发动态裁剪逻辑这套映射不是简单字符串匹配。Silicon-AI 的错误解析器会深度检查原始响应对 JSON 响应解析error.code、error.message、HTTP status code 三者组合对非 JSON 响应如某些厂商返回纯文本错误用正则匹配关键错误模式对超时类错误结合http.client的timeout异常与 HTTP status code 综合判断。更重要的是每个标准化错误都携带可操作的上下文Actionable Context。例如RateLimitError实例会包含class RateLimitError(Exception): def __init__(self, provider: str, retry_after: Optional[int] None, estimated_reset_time: Optional[datetime] None): self.provider provider self.retry_after retry_after # 原始响应中的 Retry-After header self.estimated_reset_time estimated_reset_time # 基于当前时间 retry_after 推算 super().__init__(fRate limit exceeded for {provider})业务代码因此可以写try: response llm.chat(...) except RateLimitError as e: if e.retry_after: time.sleep(e.retry_after) retry() else: # 无 Retry-After 时按指数退避 backoff min(2 ** attempt random.uniform(0, 1), 60) time.sleep(backoff) except AuthenticationError: # 触发凭证刷新流程 refresh_credentials() retry()这套体系让错误处理从“防御性编程”升级为“响应式运维”。我们在某政务热线项目中部署后LLM 相关告警量下降 67%因为 83% 的429错误被自动重试消化不再上升为 P0 告警而所有AuthenticationError都被集中路由到凭证管理模块自动触发密钥轮换人工介入率从每周 12 次降至每月 1 次。提示Silicon-AI 的错误泛化体系还包含错误影响面评估Impact Assessment。例如当ContextOverflowError发生时它不仅抛出异常还会记录被裁剪的 message 轮次、原始 token 数、裁剪后保留比例并上报到监控系统。这让我们能快速定位是某个 prompt 模板设计不合理如固定插入 500 字冗余说明还是某家模型的上下文计算存在偏差如文心一言对中文标点的 token 计数比其他家多 20%。这种数据驱动的错误分析远比单纯看错误率更有价值。4. 不是“支持五家”而是“随时接入第六家”Silicon-AI 的 Provider Adapter 架构设计“支持五家大模型”听起来像一个静态功能列表但 Silicon-AI 的真实价值在于它的可扩展性设计Extensibility Architecture。它的目标从来不是穷举所有厂商而是让团队能在 2 小时内为一家全新模型哪怕是你自研的私有模型编写出生产可用的 adapter。这背后是一套经过 17 次 provider 接入验证的模块化架构。4.1 Adapter 的最小契约四个必须实现的方法Silicon-AI 定义了一个极简的LLMProvider抽象基类任何新 provider 只需实现四个方法即可接入from abc import ABC, abstractmethod from typing import Dict, Any, Optional class LLMProvider(ABC): abstractmethod def build_request(self, messages: list, model: str, **kwargs) - Dict[str, Any]: 将标准化参数转换为 provider 特有请求体 pass abstractmethod def parse_response(self, raw_response: dict) - ChatCompletion: 将 provider 原始响应解析为标准 ChatCompletion 对象 pass abstractmethod def parse_stream_event(self, raw_chunk: bytes) - Optional[StreamEvent]: 将原始流式 chunk 解析为 StreamEventNone 表示跳过此 chunk pass abstractmethod def map_error(self, status_code: int, raw_response: dict) - Exception: 将原始 HTTP 错误映射为标准化异常 pass这个设计刻意回避了复杂继承体系。没有BaseOpenAIAdapter、BaseQwenAdapter等中间层因为实践证明每家模型的差异太大强行抽象反而增加维护成本。例如 Anthropic 的system字段和 OpenAI 的messages[0].rolesystem本质是不同范式硬塞进同一个基类会导致build_request方法里堆满if/elif。取而代之的是模板化 Adapter 开发流程。我们为每个新 provider 提供一份adapter_template.py里面预置了build_request的骨架代码标注哪些字段必须映射如model,messages,temperatureparse_response的典型结构解析示例JSONPath 表达式parse_stream_event的常见模式SSE data line 解析、JSON 解包map_error的状态码-错误类型映射表初稿。开发者只需填充具体字段名和映射逻辑无需理解 Silicon-AI 内核。某客户团队曾用这个模板在 1.5 小时内完成了对智谱 GLM-4 的 adapter 编写并通过了全部 23 个标准测试用例。4.2 配置即代码Provider 注册的声明式语法Silicon-AI 不用config.yaml或数据库存储 provider 配置而是采用Python 原生配置Python-native Configuration。每个 provider 的连接参数、超时设置、重试策略都定义为 Python 类# providers/qwen.py from silicon_ai.providers import LLMProvider from silicon_ai.config import ProviderConfig class QwenProvider(LLMProvider): def __init__(self, config: ProviderConfig): self.api_key config.api_key self.base_url config.base_url or https://dashscope.aliyuncs.com/api/v1 self.timeout config.timeout or 30 # providers/openai.py class OpenAIProvider(LLMProvider): def __init__(self, config: ProviderConfig): self.api_key config.api_key self.base_url config.base_url or https://api.openai.com/v1 self.organization config.organization # OpenAI 特有字段注册时只需导入并调用register_provider# app.py from silicon_ai import register_provider, LLMClient from providers.qwen import QwenProvider from providers.openai import OpenAIProvider # 注册 provider register_provider(qwen, QwenProvider, { api_key: os.getenv(QWEN_API_KEY), base_url: https://dashscope.aliyuncs.com/api/v1 }) register_provider(openai, OpenAIProvider, { api_key: os.getenv(OPENAI_API_KEY), organization: os.getenv(OPENAI_ORG_ID) }) # 初始化客户端 llm LLMClient(default_modelqwen-max)这种设计带来三大优势类型安全IDE 能自动补全QwenProvider的__init__参数避免 YAML 配置的运行时拼写错误环境隔离不同环境dev/staging/prod可导入不同配置模块无需修改 YAML 文件动态注入可在运行时根据业务规则切换 provider例如“金融类 query 用 Qwen创意类 query 用 Claude”。4.3 测试即文档每个 Adapter 的自动化验收测试套件Silicon-AI 强制要求每个 provider adapter 必须附带完整的test_*.py文件且测试用例覆盖五个维度测试维度示例用例验收标准基础连通性test_can_call_health_endpoint()HTTP 200响应包含{status: ok}标准 chat 调用test_chat_returns_valid_completion()response.choices[0].message.content非空response.usage字段存在流式响应一致性test_streaming_yields_incremental_content()至少收到 3 个StreamEventis_finalTrue的 event 在末尾错误映射准确性test_invalid_api_key_raises_authentication_error()401响应必须抛出AuthenticationError不能是BadRequestError边界场景鲁棒性test_long_prompt_triggers_context_overflow()10K token prompt 必须抛出ContextOverflowError而非500这些测试不是摆设。Silicon-AI 的 CI 流水线会对每个 PR自动运行新增 adapter 的全部测试对主干分支运行所有已注册 provider 的回归测试目前 17 个 provider × 5 维度 85 个用例当某家厂商 API 发生变更如 OpenAI 新增response_format参数CI 会立即失败提醒团队更新 adapter。我们在某次 OpenAI 更新gpt-4-turbo的max_tokens默认值时CI 在 12 分钟内捕获到test_chat_with_max_tokens失败自动创建 Issue 并分配给负责人。整个修复、测试、上线过程耗时 47 分钟而未使用 Silicon-AI 的兄弟团队花了 3 天才发现线上max_tokens限制失效导致的响应截断问题。注意Silicon-AI 的测试框架内置Mock Provider Server。它不是一个简单的unittest.mock而是启动一个真实的 HTTP server模拟 provider 的全部行为包括流式 SSE、各种错误状态码、header 交互。这确保了测试环境与生产环境的一致性。例如我们曾用它复现了混元 API 在Connection: closeheader 下的流式中断 bug并在上线前就修复了 adapter 的连接保持逻辑。5. 生产就绪的细节Silicon-AI 如何解决 Token 计费、并发控制与可观测性抽象层的价值最终要落在生产环境的稳定性、可审计性和可运维性上。Silicon-AI 在“接入五家模型”的表象之下埋了大量面向生产的细节设计。这些细节不炫技但决定了它能否在真实业务中存活超过三个月。5.1 Token 计费不是估算而是精确映射LLM 成本的核心是 token但各家计费口径差异巨大OpenAIprompt_tokenscompletion_tokens按整数计费Anthropicinput_tokensoutput_tokens但input_tokens包含 system prompt文心一言按“字符数”计费1 个中文字符 ≈ 2 token但实际 billing API 返回total_char_count混元按input_tokensoutput_tokens但input_tokens不包含 system prompt通义千问按input_tokensoutput_tokens但input_tokens计算方式与 OpenAI 不同对 URL、代码块等特殊 token 处理不同。Silicon-AI 的解法是双轨 Token 管理Dual-Track Token Management逻辑 Token 轨道Logical Track所有 provider 统一返回usage.prompt_tokens和usage.completion_tokens业务代码永远基于此计费物理 Token 轨道Physical Trackadapter 层记录原始 provider 的计费字段如文心的total_char_count用于与厂商账单对账。关键桥梁是Token 映射表Token Mapping Table它是一个 JSON 文件由 Silicon-AI 团队持续维护{ qwen: { prompt_token_ratio: 1.0, completion_token_ratio: 1.0, char_to_token_ratio: 2.0, system_prompt_included: false }, ernie: { prompt_token_ratio: 1.0, completion_token_ratio: 1.0, char_to_token_ratio: 2.0, system_prompt_included: true } }当文心一言返回{total_char_count: 1200}时adapter 根据映射表计算prompt_tokens 1200 / 2.0 600假设无 completion同时记录raw_billing.char_count 1200供财务对账。这套机制让财务系统只需对接 Silicon-AI 的usage字段就能生成准确账单。我们在某电商项目中上线后LLM 成本核算误差从 ±18% 降至 ±2.3%且每月对账时间从 3 人日压缩至 0.5 人日。5.2 并发控制不是全局锁而是 per-provider 的弹性限流多 provider 场景下并发控制极易陷入误区要么用全局 semaphore 一刀切导致高 QPS 的 OpenAI 被低 QPS 的文心一言拖慢要么为每个 provider 单独配置导致运维复杂度爆炸。Silicon-AI 的方案是自适应并发控制器Adaptive Concurrency Controller。它为每个 provider 维护独立的连接池并基于实时指标动态调整指标采集方式调整逻辑P95 延迟每 30 秒统计最近 100 次请求的 P95 延迟若 P95 2000ms减少 20% 并发数若 P95 800ms增加 10% 并发数上限 50错误率每 60 秒统计错误率若错误率 5%并发数降至 1持续 5 分钟后逐步恢复队列积压连接池等待队列长度若队列长度 10触发熔断拒绝新请求并返回ServiceUnavailableError控制器本身是无状态的所有状态存储在内存中避免 Redis 等外部依赖。它通过threading.local()保证线程安全且支持异步模式下的asyncio.Lock。我们在某实时翻译服务中实测当文心一言因流量高峰延迟飙升至 3200ms 时控制器在 47 秒内将其并发数从 20 降至 4同时 OpenAI 的并发数维持在 35整体服务 P95 延迟仅上升 12%而未使用该控制器的旧版本P95 延迟飙升 300%。5.3 可观测性不只是 metrics而是 traceable 的请求生命周期Silicon-AI 的可观测性设计遵循请求即实体Request-as-Entity原则。每个llm.chat()调用都会生成一个唯一的request_id并贯穿整个生命周期Logging所有日志自动附加request_id、provider、model、elapsed_ms、statussuccess/error、usageMetrics暴露 Prometheus metrics如silicon_ai_request_duration_seconds_bucket{providerqwen,modelqwen-max,statussuccess}Tracing集成 OpenTelemetry自动注入 span包含llm.request、llm.response.parse、llm.error.map等子 spanDebugging提供debug_modeTrue参数返回DebugInfo对象包含原始请求体、原始响应体、adapter 处理耗时、字段映射日志。最关键的是请求回溯Request Replay功能。当线上出现异常响应时运维人员可通过request_id在后台一键重放该请求——不是模拟而是用完全相同的参数、相同的 provider、相同的 adapter 版本重新发起调用。这让我们能在 5 分钟内确认问题是模型侧 bug如某次文心一言对特定 emoji 的解析错误还是 adapter 逻辑缺陷如某次temperature映射公式错误。提示Silicon-AI 的可观测性不依赖任何商业 APM 工具。它的 metrics endpoint/metrics直接返回 Prometheus 格式文本tracing 数据默认输出到 stdout兼容各种日志收集器

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价