Agent capability 库的可发现性工程 2026
约 24 分钟6969 字0 次阅读

把 capability 索引当作一等公民:语料、embedding、组合、降级四层叠加,再加一层可观测,Agent 才会从"知道 1000 个工具"变成"在合适的回合挑出合适的 3 个"。
一、问题的提出:为什么 Agent "知道有 1000 个工具"却选不对 3 个
当一个生产级 Agent 的工具注册中心膨胀到几百甚至上千条时,我们反复看到一个反直觉的现象:Agent 在 prompt 里清清楚楚地看到了完整工具列表,但每轮只会高亮 3-8 个相关工具——而它挑出来的那 3 个,有 30%-60% 不是用户想要的。这一现象在 LangGraph、CrewAI、AutoGen、OpenAI Agents SDK、Claude Agent SDK 五个主流框架的工程实践里都被反复复现,但归因往往被错误地归结到"模型不够强"或"context 不够长"。
真正的问题藏在工具被发现的机制里。五个主流框架在工具发现上的默认策略都偏简单:OpenAI Agents SDK 用的是工具名的关键字匹配 + 工具描述的语义类比;LangGraph 把工具发现外包给开发者自行实现的 tool_router;CrewAI 走 Agent(role, tools=[...]) 静态注入,完全不检索;AutoGen 的 GroupChat 在多智能体间靠消息旁路挑工具;Claude Agent SDK 走 Anthropic 自家的 tool_use 内置检索,默认行为接近 OpenAI Agents SDK。所有这些路径在工具数量 < 50 时几乎不暴露问题,但一旦突破 200 阈值的"工具洪流边界",每一轮选错 1-2 个工具的代价会以十倍杠杆放大——它不仅是一次调用失败,还会污染后续回合的上下文、欺骗重排序模型、把整条轨迹推向越来越远的分支。
更隐蔽的代价是"capability hallucination"——Agent 在找不到合适工具时不会沉默,而是会编造一个看起来合理但实际不存在的工具调用。生产数据里这类 hallucinated capability 的比例,在工具库过载的场景下稳定占 4%-12% 的工具调用次数,直接对应一类"模型答错但 trace 看似正常"的故障模式。
要给这件事下工程解,需要先把"工具发现"这个含糊的词拆成索引、匹配、组合、降级、可观测五层工艺,每层有自己的失败模式和度量。本文按这五层顺序拆解,目标读者是 Agent 平台工程师、LLM 应用架构师,以及任何在生产里被工具库膨胀问题烫过手的人。
二、形式化:Capability Discovery 问题的四元组与效用函数
我们形式化 "capability discovery"问题如下:
设 query 为当前轮的用户输入与对话历史的语义摘要,user_intent 为系统从 query 里推断出的高层意图(如"查询订单状态"),skill_corpus 为注册中心里的全部 skill 定义集合(每条 skill 含 name、description、schema、examples、tags),retrieval_strategy 为检索策略配置(embedding 模型、索引结构、rerank 模型、组合规则、超时预算)。那么 capability discovery 问题的求解是:
其中 是效用函数,把"这条 skill 对当前 query 与 user_intent 的有用程度"映射到一个标量分数。TopK 里的 K 由 prompt budget 决定(常见值是 3-8)。
效用函数 的工程实现通常拆成三个可加项的加权:
其中 是检索相关度(由 embedding 相似度或关键字匹配分数给出), 是用户意图命中率(由 cross-encoder reranker 或 LLM 自评给出), 是上下文兼容性分数(检查当前 schema 是否依赖尚未就绪的资源)。 通常取 0.4/0.5/0.1,但权重是次要的——真正决定系统上限的是三个分项本身的质量,而每一项在工程上都有明确的失败模式与可观测指标。
这个四元组背后有三个非平凡的设计决策:检索延迟预算、组合可行性校验、降级兜底。检索延迟预算决定了是用单 embedding 直接 topK,还是允许走"召回 + 重排"的两阶段管路;组合可行性校验决定了是返回独立 skill,还是返回可编排的 skill DAG;降级兜底决定当 embedding 服务不可用时,Agent 是否还能给出可用回答。这三个决策在第 3-6 节分别展开。
三、索引层:从关键字倒排到稠密 embedding 的三阶段路径与生产选型
索引层是 capability discovery 的"基础物理",它决定每条 skill 的语义指纹如何被存储、如何被检索。我们按工程进化顺序给出三阶段路径:
阶段一:纯关键字倒排——把 skill.name 与 skill.description 拆词,建 BM25 或 TF-IDF 倒排表,query 在运行时做同样的分词与打分。这是 0 依赖、0 成本、5ms 内返回的方案,但它对 paraphrase(改写)与 zero-shot intent(从未在 description 里出现过的意图)完全失效——user_intent="查我昨天下的单"和 skill.description="查询订单详情,需要 order_id 参数"在字面完全不相似,BM25 给 0 分。适合:工具数量 < 30、prompt budget 极紧、零依赖部署。
阶段二:稠密 embedding 索引——选一个 embedding 模型(常见候选:text-embedding-3-small、bge-large-zh-v1.5、bge-m3、cohere-embed-multilingual-v3、Qwen3-Embedding),把 skill.description + skill.examples + skill.name 三段拼接后离线编码成 768-1536 维向量,落 faiss / pinecone / weaviate / chroma 等向量库;运行时把 query 也编码,做 ANN 近邻检索。这一阶段的工程选型核心是两个问题的取舍:embedding 模型的中英能力(bge-large-zh-v1.5 与 Qwen3-Embedding 在中文里显著优于 text-embedding-3-small),向量库的运维成本(faiss 内存版适合 < 50 万条,faiss IVF 适合 50-500 万,云托管适合 > 500 万)。
阶段三:hybrid 关键字 + 稠密 + 元数据过滤——把 BM25、embedding、tag/schema/role 等结构化元数据过滤组合起来,通常权重 0.3/0.6/0.1 或 0.4/0.4/0.2。这一阶段是当前生产主流,推荐从阶段一起步,数据观察显示真匹配率不够时,逐步叠加 stage 二、stage 三。
索引层选型的三个工程决策点
决策点一:embedding 模型的中英能力权重。bge-large-zh-v1.5 在 C-MTEB benchmark 的中文 retrieval 任务上 NDCG@10 接近 65 分,显著高于 text-embedding-3-small 的 53 分;但反过来,text-embedding-3-small 在 MIRACL 多语种 benchmark 上 70 分,bge 只有 58 分。如果业务以中文为主、英文为辅,bge 系列是更稳的选择;如果业务对多语种敏感(跨境电商、国际客服),cohere-embed-multilingual-v3 与 jina-embeddings-v3 是更平衡的候选。需要警惕的是:开源模型不能光看 benchmark 数字——同一个 bge-large-zh-v1.5 在企业内部 skill 库上的真实 Recall@10 可能跌到 30-50%,因为私有词典(产品代号、内部缩写)从未在训练语料出现过。
决策点二:向量库的运维成本。faiss 内存版(IndexFlatIP / IndexHNSW)在 50 万条以下几乎零运维,适合中小规模;faiss IVF / PQ 索引在 50-500 万条需要重新训练 + 离线重建,适合中等规模;Pinecone / Weaviate Cloud / Qdrant Cloud 等托管服务则把运维外包给厂商,在 > 500 万条时通常优于自建,但代价是每条向量月费 $0.01-0.05 不等。一个生产 Agent 在 5 万条 skill 量级,faiss 内存版 + 周末全量重建是一个被严重低估的简单方案,运维负担接近于零。
决策点三:刷新频率。离线 batch 重建(每天/每周)在工具数量稳定时是首选;在线增量更新(每条 skill 注册即时落向量)在频繁变动时是必须。增量更新需要解决的工程细节包括:embedding 模型异步批量重算(每条 skill 入库后入 Kafka 队列,后台 worker 批量重算 embedding)、版本号管理(每条 skill 记录 embedding_version,query 检索时把同一版本的向量一起取)、新工具可见性 SLO(从入库到可被检索到的端到端延迟,生产常见目标是 30 秒到 5 分钟)。
关键工程判断:索引建在哪、何时刷。离线 batch 重建适合工具数量稳定、embedding 可重算的场景;在线增量更新适合工具频繁注册/下线的场景。后者需要 embedding 模型异步批量重算,版本号管理,以及"新工具出现后多久被检索到"的 SLO(常见是 30 秒到 5 分钟)。生产里我们通常选"双索引并存":一个离线索引服务常规查询,一个在线索引服务实时增量,query 路由层做结果合并去重。
下面是 Python 端的最小可用 hybrid 索引示例(对应阶段三),用 chromadb 与 rank-bm25:
import chromadb, bm25s
from sentence_transformers import SentenceTransformer
class HybridCapabilityIndex:
def __init__(self):
self.client = chromadb.PersistentClient(path="/data/skill-index")
self.coll = self.client.get_or_create_collection("skills",
metadata={"hnsw:space": "cosine"})
self.embedder = SentenceTransformer("BAAI/bge-large-zh-v1.5")
self.bm25 = None
self.corpus = []
def upsert(self, skill_id, skill_def):
text = skill_def["name"] + " " + skill_def["description"]
vec = self.embedder.encode(text).tolist()
self.coll.upsert(ids=[skill_id], documents=[text], embeddings=[vec])
self.corpus.append((skill_id, text))
def finalize_bm25(self):
texts = [t for _, t in self.corpus]
self.bm25 = bm25s.BM25()
self.bm25.index(bm25s.tokenize(texts))
def search(self, query, top_k=10, alpha=0.6):
vec = self.embedder.encode(query).tolist()
dense = self.coll.query(query_embeddings=[vec], n_results=top_k)["ids"][0]
bm = self.bm25.retrieve(bm25s.tokenize([query]), k=top_k)[0]
bm_ids = [self.corpus[i][0] for i in bm]
scores, ids = {}, set(dense) | set(bm_ids)
for r, sid in enumerate(dense):
scores[sid] = scores.get(sid, 0) + alpha * (top_k - r) / top_k
for r, sid in enumerate(bm_ids):
scores[sid] = scores.get(sid, 0) + (1 - alpha) * (top_k - r) / top_k
return sorted(scores.items(), key=lambda x: -x[1])[:top_k]
注意关键工程细节:alpha 权重不是常数,需要按 query 类型动态调整——长 query / 自然语言 query 应偏向 dense(0.7-0.8),短 query / 命令式 query 应偏向 bm25(0.3-0.4)。生产里通常加一个 query classifier 来预测这个权重。
四、匹配层:双塔向量召回 + cross-encoder rerank 的工程实现与延迟预算
召回层解决"从百万 skill 里挑出 100 条候选",但这把 100 条送进 LLM 的 prompt 里仍太宽——LLM 在工具清单里挑对的准确率随清单大小单调下降,经验值是 > 30 条工具时挑对率跌破 70%, > 80 条时跌破 50%。这就是为什么必须做 rerank。
双塔向量召回用同一个 embedding 模型把 query 与 skill 都编码成向量,效率高(QPS 上万、延迟 < 10ms),但精度有限——它只学到"两边同语义"的弱监督信号。Cross-encoder rerank把 query 与 skill 拼接后送进一个更深模型(常见:bge-reranker-large、cohere-rerank-v3、jina-reranker、Qwen3-Reranker),输出一个 0-1 的精确相关度分数,精度高(在 BEIR / MIRACL / CMRC2018 等 benchmark 上比双塔高 15-25 个百分点),但代价是 QPS 仅 50-200、延迟 100-500ms。
工程上的常见做法是两阶段管路:双塔召回 top-200,rerank 取出 top-20,送进 LLM prompt 取 top-3-8。这种管路的关键预算分配如下(以 p50 / p99 latency 目标 800ms / 1500ms 的生产 Agent 为例):
- embedding 编码 query:30ms / 80ms (CPU)/ 8ms / 20ms (GPU)
- 向量库 ANN 检索:20ms / 50ms
- 双塔打分合并:10ms / 25ms
- cross-encoder rerank top-200→top-20:200ms / 450ms
- LLM 选 top-8→top-3:500ms / 850ms
- 其他(IO、序列化、遥测):40ms / 95ms
- 合计:800ms / 1550ms,首版就逼近预算,后续优化只能靠 caching、batching、模型蒸馏
真正的工程难点不在延迟,而在 rerank 模型的领域适配。开源 reranker 在通用 benchmark 上 80%+ NDCG@10,但在企业内部 skill 库上往往会跌到 50-60%——因为企业 skill name 里大量是缩写、产品代号、内部项目名,bge 模型对这些"私有词典"零知识。解决方案:用 200-500 条人工标注 query-skill 对做 fine-tune,LoRA 微调 1-2 小时即可拉到 75-85%。这是值得投入的工程——一次微调的收益能持续 6-12 个月。
下面是 TypeScript 端的最小可用 rerank 客户端,演示如何在生产环境里把请求分阶段、并发、限流:
import { HNSW } from "hnswlib-wasm";
import axios from "axios";
interface ScoredSkill { id: string; score: number; }
export async function twoStageRetrieve(
query: string,
topK: number = 8
): Promise<ScoredSkill[]> {
const t0 = Date.now();
// Stage 1: dense recall top-200 via in-process HNSW index
const vec = await embedWithBGE(query);
const index = await HNSW.load("/data/skill-index.bin");
const candidates = await index.searchKNN(vec, 200);
console.log(`[stage1] ${Date.now() - t0}ms, ${candidates.length} candidates`);
// Stage 2: cross-encoder rerank, batched, with 800ms timeout
const pairs = candidates.map((c) => ({ id: c.id, text: `${query} [SEP] ${c.text}` }));
const rerank = await rerankWithTimeout(pairs, { topN: 50, timeoutMs: 800 });
console.log(`[stage2] ${Date.now() - t0}ms, ${rerank.length} reranked`);
// Stage 3: quick structural filter (skip skills whose deps are unavailable)
return rerank
.filter((r) => !r.skill.requiredResource || hasResource(r.skill.requiredResource))
.slice(0, topK);
}
async function rerankWithTimeout(pairs: any[], opts: { topN: number; timeoutMs: number }) {
return Promise.race([
axios.post("https://internal-reranker.internal/rerank", { pairs }, { timeout: opts.timeoutMs }),
new Promise((_, rej) => setTimeout(() => rej(new Error("rerank-timeout")), opts.timeoutMs)),
]).then((res) => res.data.scored.slice(0, opts.topN))
.catch((err) => {
// FALLBACK: if rerank times out, return top dense candidates
console.warn(`rerank degraded: ${err.message}`);
return pairs.map((p) => ({ id: p.id, score: 0.5 })).slice(0, opts.topN);
});
}
注意两个生产实践细节:rerank 失败时一定降级返回 dense 结果,不是直接抛错给上层 Agent;top-200 的批量打分而不是串行,否则延迟会被 N×per_pair 拉爆。
五、组合推荐层:从单 skill 召回到多 skill 组合的 DAG 编排与可行性校验
单 skill 召回够用吗?在多数对话 Agent 是的;但在 Agentic Workflow(订单处理、报销审批、跨系统数据同步)等场景下,单 skill 无法完成 user_intent,需要 2-5 个 skill 编排成一个 DAG。这一层的工程复杂度比召回高出一个量级。
多 skill 组合的形式化:给定 user_intent 与 top-20 候选 skill,目标是找出一个子集 (大小 2-5)与一个编排计划 (节点为 skill 调用,边为数据依赖),使得 协作执行后可以覆盖 user_intent,且 在当前 schema 与环境约束下是可执行的。
工程上有两条路径:
路径一:静态 DAG 模板优先——把企业内部常见的 high-frequency workflow 预先编排成 template(下单流程、退款流程、跨系统对账流程等),每个 template 标注它依赖的 skill 子集,运行时先做 template 召回,把 template 命中的 skill 子集强制注入 prompt。这种路径适合"业务相对固定、模板复用率高"的企业 Agent,准确率最高,但维护成本是"每次业务变更都要更新模板库"。
路径二:LLM 提议 + DAG 校验——让 LLM 从 top-20 skill 候选里自由提议 2-5 个 skill 的组合,后端用一个 DAG validator 检查提议的可行性:节点依赖是否成环、所需参数是否齐备、所需环境资源是否就绪、超时预算加总是否超出 SLO。这一路径灵活度高,但准确率依赖 LLM 自身的"是否懂业务流"能力。
下面是 DAG validator 的最小可用 Python 实现:
from dataclasses import dataclass
from typing import Dict, List, Set
from collections import defaultdict, deque
@dataclass
class SkillNode:
name: str
inputs: List[str] # required input params
outputs: List[str] # produced output params
deps: List[str] # skill names this node depends on
resource: str = "" # optional required env resource (db, file, etc)
class DAGValidator:
def __init__(self, available_resources: Set[str]):
self.res = available_resources
def validate(self, plan: List[SkillNode]) -> tuple[bool, str]:
# 1. cycle check via Kahn's algorithm
indeg = {n.name: 0 for n in plan}
graph = defaultdict(list)
for n in plan:
for d in n.deps:
if d not in indeg:
return False, f"unknown dep {d}"
graph[d].append(n.name)
indeg[n.name] += 1
q = deque([n for n in plan if indeg[n.name] == 0])
topo = []
while q:
cur = q.popleft()
topo.append(cur)
for nb in graph[cur.name]:
indeg[nb] -= 1
if indeg[nb] == 0:
q.append(nb)
if len(topo) != len(plan):
return False, "cycle detected"
# 2. resource check
for n in plan:
if n.resource and n.resource not in self.res:
return False, f"resource {n.resource} unavailable for {n.name}"
# 3. parametric feasibility — outputs of upstream cover inputs of downstream
produced = set()
for n in topo:
for inp in n.inputs:
if inp not in produced:
return False, f"unbound input {inp} for {n.name}"
produced.update(n.outputs)
return True, "ok"
这个 validator 是工程上最被低估的一块。很多 Agent 团队跳过了 cycle 检查、参数检查、资源检查,导致 LLM 提议的"看上去合理"的 skill 组合跑起来才暴雷——比如 N 个 skill 调完之后输出参数才覆盖 N+1 个 skill 的输入,但 N+1 个 skill 又需要 N+1 时已经 rollback 的某个中间产物。这种参数-时序错位会让 Agent 永远无法收敛,而 trace 日志完全看不出问题。
生产里我们见过三类更隐蔽的 DAG 失败:第一类,资源 schema 隐藏冲突——skill A 说需要 db.orders 这张表,skill B 说需要 db.orders_v2,LLM 提议两条都用,DAG validator 只校验参数不管物理 schema,执行时 db 查询 zero rows。第二类,异步副作用排序错误——skill A 异步触发一个 audit log 写入,skill B 立刻读这个 log,DAG 拓扑合法但时序上 audit log 还没落盘,skill B 拿到 stale 数据。第三类,超时预算 silent overflow——每个 skill 都声称自己 2 秒超时,DAG 串行编排 5 个 skill 后总超时 10 秒,但系统的 Agent SLO 是 8 秒,DAG validator 不做超时预算的加总会让 Agent 90% 超时。
应对这三类失败,validator 必须扩成 5 项硬检查:(1) 拓扑 cycle (2) 参数时序 (3) 资源兼容 (4) 物理 schema 兼容(db.schema、api.version、file.format 三类元数据可在注册中心汇总) (5) 总超时预算加总(每 skill 标 p99 latency,DAG 编排后 sum 必须 ≤ Agent SLO × 0.7 留 buffer)。这 5 项任一项缺失,生产里都会以"Agent 不收敛但 trace 看似正常"的形态冒出来。
六、冷启动与降级层:无 embedding 语料环境的兜底策略
不是每个 Agent 团队都从第一天起就有 1000 条技能数据。下面三阶段兜底策略是经过多个 0-1 项目验证的"无 embedding 也能跑"路径:
阶段一:LLM 直选(0-30 条 skill 范围)——prompt 里直接列全部 skill.name + skill.description(总 token 通常 < 1500),让 LLM 自己挑。这种"零工程"路径在 30 条 skill 内准确率可达 90%+,非常适合 MVP 阶段。缺点:> 30 条后 token 开销爆炸,准确率断崖下跌。
阶段二:keyword + LLM 自评 + 规则过滤(30-200 条 skill 范围)——用纯关键字匹配(BM25 或 LIKE)召回 top-30,再让 LLM 在 30 条里挑 top-3-8。这一阶段不依赖 embedding 服务,部署最简。关键技巧:给 LLM 一个清晰的挑选 rubric(必须满足"对当前 query 直接相关 / 用户明确指定 / 无 schema 致命冲突才选"),而不是让 LLM 自由发挥。
阶段三:embedding 索引 + rerank + DAG validator(> 200 条 skill 范围)——这就是第 3-5 节描述的完整管路。从阶段二迁到阶段三的契机通常是:用户开始抱怨"Agent 总是挑错工具"——这时投入 embedding 模型微调 + rerank 部署的工程 ROI 才能跑正。
降级管路还需要一条关键的工程实现:检测 embedding 服务的健康度,在 embedding 服务 down 时自动切回阶段二。一个轻量实现:每次 embedding 服务的 p99 延迟超 300ms 或错误率 > 1% 时,自动把检索路由切到 BM25 fallback,记录到 metrics,告警给平台团队。下面是健康探测 + 路由切换的最小 TypeScript 实现:
class CapabilityRouter {
private denseHealthy = true;
private bm25 = new BM25Fallback();
private embedder = new EmbeddingClient();
private probeTimer: NodeJS.Timeout;
start(): void {
this.probeTimer = setInterval(() => this.probe(), 30_000);
}
async retrieve(query: string, k: number): Promise<ScoredSkill[]> {
if (this.denseHealthy) {
try {
return await this.embedder.search(query, k, { timeoutMs: 300 });
} catch (e) {
this.markDenseDegraded(e);
}
}
return this.bm25.search(query, k);
}
private async probe(): Promise<void> {
const t0 = Date.now();
try {
await this.embedder.search("__healthcheck__", 1, { timeoutMs: 300 });
if (!this.denseHealthy) this.markDenseRecovered();
} catch {
this.markDenseDegraded(new Error("probe-failed"));
}
}
private markDenseDegraded(e: Error): void {
if (!this.denseHealthy) return;
this.denseHealthy = false;
metrics.increment("capability.router.degraded", { reason: e.message });
console.warn(`[router] dense degraded to BM25, reason=${e.message}`);
}
private markDenseRecovered(): void {
this.denseHealthy = true;
metrics.increment("capability.router.recovered");
console.info("[router] dense recovered");
}
}
注意 markDenseDegraded 与 markDenseRecovered 的对偶设计——生产里只 degrade 不 recover 是经典的"半失败"模式,业务长期跑在降级路径上没人发现,直到新模型上线才暴露问题。
七、可观测性层:四指标 + 一组反模式
Capability discovery 系统的可观测性必须独立于普通 LLM trace,因为它的失败模式比后者更隐蔽。下面四指标是一组经验组合:
指标一:Recall@K 与 MRR——用人工标注集(50-300 条 query-skill 对)离线周期评估;Recall@K 衡量 top-K 是否包含 ground truth skill,MRR 衡量 ground truth 在 top-K 里的位置。线上也可加隐式反馈:用户主动修改工具调用参数,可推断原推荐 tool 错了;Agent 触发某 tool 后用户立刻追问,可推断该 tool 没满足 user_intent。
指标二:Acceptance Rate——top-3-8 里 LLM 实际挑了几个进入调用。正常值是 1-3 个 tool per turn,如果 LLM 频繁挑 6 个以上,说明候选里"水分"太多,rerank 质量下降;如果频繁全 0 挑,说明检索质量塌了。
指标三:Failed Tool Rate——top 推荐进入调用后的失败率(超时 / 4xx / 5xx / 参数错误)。如果某 skill 的失败率突增,可能是检索把它推到了不该用的场景,需要重新标定它的 description 与 examples。
指标四:Retrieval Latency p50 / p99——这三段延迟必须分开报(embedding 召回 / rerank / LLM 选),不要报全集。生产里发现 rerank 是最常爆发的瓶颈——一旦 rerank 服务扛不住,必须走 fallback 而不是把整条管路拖延。
反模式一:把检索 Recall 当作唯一目标——过度优化 Recall 会把 top-K 灌满"看起来相关但用户用不到"的 skill,Acceptance Rate 暴跌。反模式二:不监控降级路径——只看到"全部查询都成功返回 top-K"是不够的,要看其中多少走的是 BM25 fallback。反模式三:用全局 LLM judge 评估检索质量——LLM judge 在 75% 准确率区间就和检索系统互相"循环验证",应优先用人工标注 + 隐式用户反馈。
下面是一个生产可观测性的 mermaid 流向图(对应 recall/acceptance/failed/latency 四个 metric 的采集与告警路径):
图表加载中…
这张图的关键在于:让每条 metric 都能溯源到一个用户决策点,而不仅是计数。AcceptanceRate 升高时去查用户改了什么,RetrievalMismatch 升高时去查 query 的语义类型分布变化,FailedToolRate 升高时去查工具服务端的 status code 分布。
八、生产踩坑:8 个真实工程雷区
雷区 1:embedding 漂移导致召回静默退化——embedding 模型升级后,旧索引的向量与新模型的 query 向量不在同一坐标系,Recall@10 断崖下跌但所有 metric 都"绿",因为发布是渐进 rollout。对策:每次升级 embedding 模型必须重建索引,且同时跑 A/B 双索引 query 的检索分布对比。
雷区 2:工具重名导致 LLM 误选——多个 skill 叫类似的 name(如 search_orders 与 search_order_history),LLM 经常挑错。对策:在 skill 注册阶段强制 name 全局唯一,description 用一句话说清差异。
雷区 3:description 写得抽象导致 LLM 不会挑——"perform various operations on customer data" 这种描述,LLM 在每个查询都把它当 backdrop 选。对策:description 必须包含具体的 input/output 示例,字数 ≤ 200 字但例子 ≥ 2 个。
雷区 4:长 schema 把 token 打爆——某些 skill 的 input schema 用 JSON Schema 嵌套 6 层,展开后 2000+ token,挑一个 skill 就吃掉 LLM 全部预算。对策:schema 必须按需折叠,长 schema 用 reference document 在 prompt 里只放概要。
雷区 5:fallback BM25 与 dense 索引的得分不可比——BM25 得分是稀疏相关性,dense 得分是余弦相似度,二者物理意义不同,直接加权会误导。对策:分别做 min-max 归一化后再加权,或者只取 top-K 不加权融合。
雷区 6:capability hallucination 无法监控——Agent 调了一个不存在的 tool(可能是 LLM 编的,可能是 schema typo),trace 只显示"tool call failed not_found"。对策:在工具执行层 hook 一个 schema-validate step,任何 tool name 不在 registry 里的调用立即记录 + 告警。
雷区 7:跨语言 embedding 性能悬崖——很多企业内部 skill 库中文英文混杂,bge 系列在双语下表现尚可,但 text-embedding-3-small 在纯中文场景显著退化。对策:实测 MS MARCO 与 C-MTEB 两个 benchmark 在自家语料上的 Recall@10,再决定 embedding 模型。
雷区 8:rerank 服务的冷启动长尾——cross-encoder reranker 加载到进程需要 5-30 秒,首次请求会被认为低质量结果。对策:服务启动后立即跑一次预热 batch,把 top-2000 高频 query 与对应 skill 都跑一遍 rerank,缓存到 KV;冷启动后第一秒的查询走 cache 兜底。
九、给 Agent 平台工程师的 6 条工程清单
最后把全文压成可执行的 6 条清单:
- 从最简的 stage 一(纯 BM25)起步,数据观察驱动升级——不要在第一版就上 embedding 索引 + rerank + DAG validator。先用 BM25 + LLM 直选跑 1-2 周,收集 query 真实分布,再决定是否升级。
- 建立人工标注集,周期评测 Recall@K 与 MRR——每两周一批 50-100 条新 query-skill 标注,把检索质量的提升与回归量化,不要凭感觉调 embedding 模型。
- rerank 必须做企业语料微调——一次 LoRA 微调能让 NDCG@10 提升 15-25 个百分点,持续受益 6-12 个月,是工程 ROI 最高的单点投入。
- DAG validator 是 multi-skill 编排的护城河——cycle 检查、参数可行性、资源检查、超时预算是四项硬规则,任何一项缺失都会让 Agent 在某类 query 上永远不收敛。
- 降级与健康探测必须对偶——只 degrade 不 recover 是"半失败"模式,业务长期跑在 BM25 路径上没人发现。每 30 秒 probe + 告警。
- 可观测性按决策点分组,而不是按组件分组——Acceptance / RetrievalMismatch / FailedTool / RetrievalLatency 四指标对应四个用户行为分支,比"embedding 服务 QPS"这类运维指标更接近产品质量。
我们相信 capability discovery 是 Agent 工程里被严重低估的子领域。当工具库突破 100 条,这件事就不再是"模型选哪个 tool"的 prompt 工程,而是一套独立的检索 + 编排 + 可观测系统。把它当作一等公民,而不是 LLM 上下文里的一个黑盒附件——这是 Agent 从 demo 走向生产的关键一步。
参考文献
- Lewis P, et al. Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks. NeurIPS 2020.
- Santhanam K, et al. ColBERTv2: Effective and Efficient Retrieval via Lightweight Late Interaction. NAACL 2022.
- Chen J, et al. BGE M3-Embedding: Multi-Lingual, Multi-Functionality, Multi-Granularity Text Embeddings Through Self-Knowledge Distillation. arXiv 2024.
- Xiao S, Liu Z, et al. C-Pack: Packaged Resources To Advance General Chinese Embedding. arXiv 2023.
- Reimers N, Gurevych I. Sentence-BERT: Sentence Embeddings using Siamese BERT-Networks. EMNLP 2019.
- Robertson S, Zaragoza H. The Probabilistic Relevance Framework: BM25 and Beyond. Foundations and Trends in IR 2009.
- Karpukhin V, et al. Dense Passage Retrieval for Open-Domain Question Answering. EMNLP 2020.
- Johnson J, Douze M, Jégou H. Billion-scale similarity search with GPUs. IEEE Transactions on Big Data 2021.
- LangChain Inc. OpenLLMetry — OpenTelemetry for LLM Applications. GitHub repository, accessed 2026-07.
- Anthropic. Claude Agent SDK — Tool Use and Computer Use Reference. Anthropic Docs, accessed 2026-07.
- OpenAI. OpenAI Agents SDK — Function Calling and Tool Schemas. OpenAI Platform Docs, accessed 2026-07.
- LangChain. LangGraph Documentation — Tool Routing and State Management. LangChain Docs, accessed 2026-07.
- Microsoft Research. AutoGen: Enabling Next-Gen LLM Applications via Multi-Agent Conversation. COLM 2024.
- CrewAI Inc. CrewAI Documentation — Agent Tools and Task Delegation. CrewAI Docs, accessed 2026-07.