Portkey 网关多供应商兜底:一个 API 地址背后挂三家模型

你的业务把命运押在一家模型 API 上,迟早会遇到一次:它限流、它涨价、它抽风返回空。前面放一层网关,把多家供应商挂在同一个 OpenAI 兼容地址后面,是性价比极高的保险。Portkey 是这类网关里上手较快的一个。 模型网关到底解决什么问题? 不是又一个聚合 API,而是三件事:故障自动兜底,主供应商超时就切备线;按规则路由,简单问题走便宜模型、复杂问题走强模型;以及统一可观测,所有请求的 token、延迟、错误集中在一处。上层代码只认一个 base URL。 最小配置该怎么搭? 起一个 Portkey 实例,配置里登记多家供应商的密钥,定义一条策略:默认走主模型,连续失败两次或超过超时阈值就切到备用,备用再失败切第三家。设置重试次数与退避间隔。对上层应用,请求格式完全是 OpenAI 兼容的,改一个 base URL 就接上了。 多模型路由策略怎么定? 别让所有请求都走最贵的模型。按任务复杂度分桶:分类、摘要、抽取这类结构化任务走便宜模型;创作、推理走强模型。网关支持按请求内容或元数据路由,这套分流本身就能把账单砍掉一截。 自动兜底有什么隐患? 自动兜底会掩盖问题——主供应商明明在持续故障,被备线悄悄兜住,你可能几周都没发现。务必打开告警:当兜底触发次数超过阈值就通知,让你知道主链路出了状况,而不是被备线蒙在鼓里。 密钥和账单该怎么管? 多家供应商的密钥统一收在网关节点,别散落在各个业务服务里,方便轮换和吊销。更实用的是账单视角:网关把所有请求归一后,你能清楚看到每个模型实际被调了多少次、花了多少。很多团队第一次看这张表才发现,一个没人维护的旧接口每天还在偷偷调用最贵的模型——关掉它就是纯利润。这种盲区,不用网关你永远看不见。 多供应商兜底值不值得现在上? 多供应商兜底不是大企业专属,一个网关配置就能让你的智能体在某家模型抽风时不挂。把它当成基础设施保险,成本极低,关键时刻能保住一整个下午的在线服务。 去论坛讨论 这篇文章里的观点,欢迎到硅基AGI论坛讨论。按标题搜索:Portkey网关

2026-10-09 · 1 min · 31 words · AI 实战派

给你的 Agent 装个黑匣子:Langfuse 调用追踪实战

单轮聊天出错,看一眼输出就知道问题。但 Agent 跑十几步、调了五六个工具、还把上下文越滚越长之后,用户一句「结果不对」甩过来,你根本无从下手——它当时到底检索了哪段文档、调了哪个工具、中间哪一步开始跑偏?这就是为什么 2026 年做 Agent,可观测性是必需品,不是锦上添花。Langfuse 是这一范畴里常用的开源选择。 它解决什么 Langfuse 的核心是 trace:一次 Agent 运行从开始到结束,被记成一棵树。每个节点记录输入、输出、耗时、token 数、错误。你可以在网页上回放:这一步模型看到的上下文是什么,那一步工具返回了什么。 三步接进来 自托管一行 docker 起 Langfuse,或用云版拿一对公钥私钥; 在你的 Agent 代码里装它的 SDK,用装饰器或上下文管理器包住模型调用与工具函数; 给每步打上 name 和元数据(任务 id、用户 id),跑几次,控制台就能看到轨迹树。 对用 OpenAI Agents SDK、LangChain、Dify 的项目,Langfuse 都有现成集成,基本是填两个环境变量的事。 怎么用起来 复现 bug:用户报障时,按任务 id 拉出那一次 trace,定位是检索错了还是工具返回错了; 找成本黑洞:按 token 排序,找出那几个每轮都把超长上下文重发的步骤,针对性做压缩; 回归对比:改完提示词,对比新旧 trace 的步数与失败率。 判断 Agent 从 demo 走向生产,最后一公里往往不是「它够不够聪明」,而是「它出错时你能不能修好」。给 Agent 装上 Langfuse 这个黑匣子,等于给飞行记录仪——飞得多高是模型的事,坠机了能黑匣子读数,才是工程团队的底气。 补充一个进阶用法:给 trace 打标签。按部门、按场景、按用户等级打标签,事后就能对比「市场部的 Agent 平均跑几步、花多少 token」和研发的差异。这种横向对比,往往能直接定位到那个一直没被注意的高成本流程——它跑得勤,却没人意识到它在烧钱。 去论坛讨论 这篇文章里的观点,欢迎到硅基AGI论坛讨论。按标题搜索:Agent观测

2026-10-08 · 1 min · 63 words · AI 实战派
ai monitoring stack

AI 应用监控体系:从模型质量到基础设施

监控分层架构 AI 应用监控需要三层视角,每层关注不同问题: ┌─────────────────────────────────────────────────┐ │ Layer 3: 模型质量监控 │ │ - 回答正确率 / 幻觉率 / 毒性检测 / 偏见 │ │ - 用户满意度反馈 │ ├─────────────────────────────────────────────────┤ │ Layer 2: 应用性能监控 │ │ - 首字延迟 (TTFT) / 完整延迟 / 吞吐量 │ │ - Token 消耗 / 成本 / 缓存命中率 │ ├─────────────────────────────────────────────────┤ │ Layer 1: 基础设施监控 │ │ - GPU 利用率 / 内存 / 网络 / 磁盘 │ │ - API 错误率 / 限流次数 / 超时 │ └─────────────────────────────────────────────────┘ 指标设计 核心指标清单 层级 指标 类型 告警阈值 模型 回答质量评分 Gauge < 0.8 模型 幻觉率 Gauge > 5% 模型 用户点赞率 Gauge < 70% 应用 TTFT (首字延迟) Histogram P99 > 2s 应用 完整延迟 Histogram P99 > 15s 应用 Token 消耗 Counter 日预算 120% 应用 缓存命中率 Gauge < 15% 基础设施 GPU 利用率 Gauge > 90% 基础设施 API 错误率 Gauge > 1% 基础设施 限流次数 Counter > 100/min Prometheus 指标定义 from prometheus_client import Counter, Histogram, Gauge, Summary # === 模型质量指标 === llm_quality_score = Gauge( "llm_quality_score", "Average answer quality score (0-1)", ["model", "task_type"] ) llm_hallucination_rate = Gauge( "llm_hallucination_rate", "Hallucination rate detected", ["model"] ) llm_user_feedback = Counter( "llm_user_feedback_total", "User feedback counts", ["model", "feedback"] # feedback: positive/negative ) # === 应用性能指标 === llm_ttft = Histogram( "llm_time_to_first_token_seconds", "Time to first token in seconds", ["model"], buckets=[0.1, 0.3, 0.5, 1, 2, 5, 10] ) llm_total_latency = Histogram( "llm_total_latency_seconds", "Total request latency in seconds", ["model"], buckets=[0.5, 1, 2, 5, 10, 20, 30, 60] ) llm_tokens_total = Counter( "llm_tokens_total", "Total tokens consumed", ["model", "direction"] # direction: input/output ) llm_cost_total = Counter( "llm_cost_usd_total", "Total cost in USD", ["model"] ) llm_cache_hit_rate = Gauge( "llm_cache_hit_rate", "Cache hit rate", ["cache_type"] # cache_type: exact/semantic ) # === 基础设施指标 === gpu_utilization = Gauge( "gpu_utilization_percent", "GPU utilization percentage", ["gpu_id", "node"] ) api_error_rate = Gauge( "llm_api_error_rate", "LLM API error rate", ["provider", "error_type"] ) 指标采集 中间件式采集 import time from contextlib import asynccontextmanager class LLMInstrumentation: def __init__(self, registry): self.registry = registry @asynccontextmanager async def trace_request(self, model: str, task_type: str): request_id = generate_id() start = time.monotonic() first_token_time = None async def on_first_token(): nonlocal first_token_time first_token_time = time.monotonic() try: yield {"request_id": request_id, "on_first_token": on_first_token} # 请求成功 elapsed = time.monotonic() - start llm_total_latency.labels(model=model).observe(elapsed) if first_token_time: ttft = first_token_time - start llm_ttft.labels(model=model).observe(ttft) except Exception as e: api_error_rate.labels( provider=model, error_type=type(e).__name__ ).inc() raise def record_tokens(self, model, input_tokens, output_tokens): llm_tokens_total.labels( model=model, direction="input" ).inc(input_tokens) llm_tokens_total.labels( model=model, direction="output" ).inc(output_tokens) # 成本计算 cost = self._calc_cost(model, input_tokens, output_tokens) llm_cost_total.labels(model=model).inc(cost) def _calc_cost(self, model, in_tok, out_tok): prices = { "gpt-4o": (0.0025, 0.01), "gpt-4o-mini": (0.00015, 0.0006), "claude-sonnet": (0.003, 0.015), } in_price, out_price = prices.get(model, (0, 0)) return (in_tok * in_price + out_tok * out_price) / 1000 使用方式 instrument = LLMInstrumentation(registry) async def chat_completion(messages, model="gpt-4o"): async with instrument.trace_request(model, "chat") as ctx: response = await llm_client.chat.completions.create( model=model, messages=messages, stream=True, ) async for chunk in response: if chunk.choices[0].delta.content: if not ctx["first_token_called"]: await ctx["on_first_token"]() ctx["first_token_called"] = True yield chunk.choices[0].delta.content instrument.record_tokens(model, input_count, output_count) 质量监控:LLM-as-a-Judge class QualityMonitor: def __init__(self, judge_model="gpt-4o", sample_rate=0.1): self.judge_model = judge_model self.sample_rate = sample_rate async def maybe_evaluate(self, query, response, context=None): """按采样率随机评估回答质量""" import random if random.random() > self.sample_rate: return score, issues = await self._judge(query, response, context) llm_quality_score.labels( model=self.judge_model, task_type="auto" ).set(score) if "hallucination" in issues: llm_hallucination_rate.labels( model=self.judge_model ).inc() async def _judge(self, query, response, context): prompt = f"""评估以下回答的质量。 问题:{query} 回答:{response} 参考资料:{context or '无'} 请检查: 1. 事实准确性(是否有幻觉) 2. 回答完整性 3. 逻辑一致性 输出 JSON:{{"score": 0.0-1.0, "issues": ["..."]}}""" result = await llm_client.chat.completions.create( model=self.judge_model, messages=[{"role": "user", "content": prompt}], response_format={"type": "json_object"}, ) data = json.loads(result.choices[0].message.content) return data["score"], data.get("issues", []) Grafana 仪表板 关键 Panel 配置 Panel 1: 延迟分布(P50/P90/P99) ...

2026-06-25 · 4 min · 789 words · AI 实战派
🔗 硅基 AGI 论坛 · silicon-agi.com | 📡 RSS 订阅
鲁ICP备2026018361号