抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

知识库页面显示“今天更新”,不代表用户现在问问题能检索到今天的内容。源文档变更后,要经过事件发现、抓取、解析、embedding、索引发布、复制与缓存失效。任何一段积压,答案都可能继续引用旧版本。

我把新鲜度定义为 query-visible time - source event time,再拆成各阶段延迟预算。没有源事件时间时,只能报告“平台最近一次成功观测”,不能把抓取时间冒充内容真实更新时间。

知识新鲜度预算拆解

很多知识库上线时只做了导入:抓文档、切块、算向量。过几个月后,同一制度的新旧版本一起命中,已离职人员的私有文档仍能被检索,源文件删除了,向量库里还留着片段。问题不在 RAG 算法,而在内容从来没有生命周期。

我把摄取当成一条可发布的数据管道。源系统的 create、update、delete、ACL change 都变成有版本的事件;新索引先在 staging 校验,再切换 current alias;删除和撤权先用 tombstone 立即阻断查询,物理清理由后台完成。

RAG 内容从源版本到索引发布

传统 API 监控里,一条 HTTP 请求通常对应一个服务操作。LLM 应用不一样:用户问一句话,内部可能先检索两次、调用主模型、执行工具、再调用模型总结;上游断流后还可能换模型重试。只记录入口 latency 和 status=200,看不出回答为何错,也算不清真实成本。

我把 run 定义成一次用户意图的完整处理,下面挂 retrieval、model attempt、tool call、policy decision 和 finalization。HTTP request 是承载方式之一,不是业务身份。所有事件通过 run_id 串起来,最终状态由证据归并得出。

一次 LLM Run 的证据树

RAG 产品加上“参考资料”区域后,答案看起来会可信很多。实际抽查时经常发现:链接确实相关,却没有支持模型写出的数字;文档说的是测试环境,答案扩展到了生产;三条引用里两条互相冲突,模型选了表达最顺的一条。

引用是展示形式,证据关系才是可验证结构。我会在最终答案前增加 Evidence 层:先拆出可核查 claim,再把每个 claim 与具体 source/version/span 对齐,标注支持、冲突或不足,最后由发布策略决定保留、降级措辞或拒答。

答案层之外增加 Claim-Evidence 层

多接几家模型后,网关很容易加一条“失败就切下一个”的逻辑。它能缓解单供应商不可用,却不是完整路由:备用模型可能不支持 function calling、上下文不够、数据地域不合规,或者虽然 HTTP 成功,业务输出已经无法使用。

我把路由分成两步。先用硬约束过滤出“有资格执行”的模型,再在候选中按具体任务的成功率、延迟和成本选择。fallback 也必须重新通过硬约束,不能为了可用性把安全与能力要求降掉。

多模型路由从请求约束出发

给 Agent 配一份“可用工具列表”,只能说明模型可以看到哪些函数,不能说明用户有权对哪些对象做什么。一个用户能调用 rerun_job,不代表他能重跑所有项目、所有环境的任务;能调用 query_table,也不代表能读每张表的每个字段。

我把工具权限表达为 subject × action × resource × context。主体来自可信会话,动作来自已注册工具,对象必须解析为稳定 ID,上下文包含租户、环境、时间和资源当前状态。模型只提出参数,不参与最终授权结论。

Agent 工具权限落到动作与对象

RAG 团队常用一张端到端准确率报表:问题发进去,答案对就记 1,错就记 0。这个数适合看整体趋势,却不能告诉我们该改 embedding、chunking、reranker、Prompt 还是知识源。

我会把评测拆成三层。第一层只测检索是否拿到充分证据;第二层固定正确证据,只测模型能否组织出正确、受引用支持的答案;第三层才跑真实端到端链路,验证两者组合后的延迟、成本和失败分布。

检索与回答分开评测,失败才能归因

Prompt 从十几行增长到几百行后,很容易变成一份“谁都能改、没人敢删”的线上配置。有人为了修一个格式问题加两句指令,第二天工具选择率下降;回滚时只把文本改回去,结果仍和上周不同,因为模型别名、工具 schema 或检索模板已经变了。

我不把 Prompt 单独视为版本单位。真正决定一次模型行为的是完整运行组合:system prompt、消息模板、模型 snapshot、sampling 参数、工具定义、检索/上下文策略和输出 parser。它们一起形成不可变 Bundle,发布与回滚都指向 Bundle ID。

Prompt 变更按软件版本发布

SQL Copilot 最容易做成“代码补全器”:模型返回 SQL,编辑器高亮一下,用户自己运行。到了需要自动修复、自动解释或一键执行的场景,这个边界就不够了。模型写出的 SQL 往往像标准 SQL,真正交给 Flink、Spark、Hive 或 PostgreSQL 时,方言、Catalog 和执行计划都会给出不同结论。

我把验证分成三段:目标引擎 Parser 证明语句属于这个方言,Catalog/Analyzer 证明对象和类型能解析,Planner/受控执行证明计划风险和结果语义可接受。任何一段失败,都返回具体证据,而不是笼统说“SQL 可能有问题”。

SQL Copilot 的三段验证

模型开启 stream=true 后,客户端能更早看到文字,接口看起来只是从一次 JSON 响应变成多次回调。实现里最常见的 bug,是每收到一个网络 chunk 就 JSON.parse,或者把每个 data: 行当成完整业务消息。

TCP/HTTP 传输分片、SSE 事件和模型增量是三层边界。它们恰好可能对齐,但协议从未保证对齐。我会先把字节流解析成完整 SSE event,再把供应商 event 归一化,最后由状态归并器构造答案。

流式响应按事件归并,不按 TCP chunk 拼字符串

模型网关第一版通常很像反向代理:统一鉴权,替换 endpoint,把 messages 转给不同供应商。真正接入多个模型后,会发现最难的不是请求字段,而是每家对流式增量、结束原因、工具调用、错误和用量的表达都不同。

如果网关只输出一套“看起来统一”的 JSON,却不保存转换前证据,线上出现空回答、工具参数残缺或 HTTP 200 后中途断流时,无法判断问题来自供应商、适配器还是客户端。我会同时保留原生协议轨和统一语义轨,两者用同一个 request ID 关联。

模型网关的双轨协议审计

OpenAI 昨天发布 Function Calling 后,模型与外部工具之间终于有了比“在文本里吐一段 JSON”更明确的接口:开发者描述函数,模型可以选择函数并生成参数。这个能力会明显降低 Agent 工具接入成本,但它解决的是参数生成,不是安全执行。

我会把模型输出叫作 tool call proposal。它必须经过协议、业务、权限和风险检查,才会变成真实调用。少了这层执行契约,JSON 越稳定,错误动作反而越容易自动落地。

Function Calling 到真实工具之间的执行契约