几乎每个深度使用过大模型 Agent 的开发者,都遇到过这种让人头疼的场面:你让模型去查一家公司的财务指标,再跟行业均值比一比,最后给个结论。结果它要么跳过查询,直接凭“记忆”编了一个数;要么查完第一个数据后,第二个要求就忘了;要么工具调用了一堆,路径却完全错误,最后答非所问。
问题往往不在单个模型能力上,而在“多跳推理 + 工具调用 + 信息检索 + 策略约束”这条完整链路上。我们缺少的正是一个能把这条链路系统化评测的框架。VAKRA 就是针对这个缺口提出的评测思路,它把 APIs、Retrieval、Multi-Hop Reasoning 和 Tool-Use Policies 放到同一条测试流水线上,让我们不只关心模型“答得对不对”,更关心它“会不会调用工具、按什么策略调用、调用错了会怎样”。
这篇文章会先讲清楚 VAKRA 到底在测什么、为什么这类评测比传统问答评测更难,再以一个可运行的最小 Python 示例,演示如何构造一个多跳工具调用任务、如何在代码里落地工具使用策略,最后给出链路调优和工程落地的建议。如果你正在做 Agent 开发、RAG 应用或模型评测体系,这篇文章可以帮你建立一套更贴近真实生产的评测视角。
1. 为什么需要 VAKRA:Agent 评测的缺口
先看一组常见评测的边界。传统大模型基准比如 MMLU、GSM8K,测的是知识储备和单步数学推理,它们基本不涉及外部工具;RAG 类基准侧重检索质量,看召回片段是否相关;Agent 类基准则考察工具调用,但多数任务停留在“调用一次工具”的层面。
真实业务场景很少这么简单。一个金融分析 Agent 的典型任务可能是:先在公司知识库里检索最新的财报发布日期,再调用财报接口拿营业收入和营业成本,计算毛利率,然后从行业报告里检索平均毛利率,最后综合判断公司竞争力。整个过程至少包含四次工具或检索动作,而且每一步的结果都会影响下一步的选择。如果模型在第三步把营收和成本搞反,最终判断就是错的;如果模型跳过检索直接回答,答案可能一本正经地胡说八道。
VAKRA 的价值在于它把这四件事统一进一个评测框架:
- Multi-Hop Reasoning:考察模型能否用多个已知信息逐步推导,而不是一步到位的“记忆检索”。
- APIs:考察模型是否理解工具的语义、参数格式,以及多步调用之间的依赖关系。
- Retrieval:考察模型能否在上下文不可信的片段中筛选正确信息,并决定下一步检索什么。
- Tool-Use Policies:考察模型是否遵守调用规则,例如最多调用几次、哪些工具必须用、哪些关键词禁止进入上下文、超出预算时如何终止。
如果只看表面,很容易误以为 VAKRA 只是又一个 Agent 排行榜。但更准确的判断是:它把评测重心从“结果正确”扩展到了“过程合规”。在业务系统里,结果正确但调用链混乱的 Agent,后期几乎无法排查;调用链清晰但结果差一点的 Agent,反而可以通过调整提示词或约束逐步优化。这种“过程 + 结果”双重评测的思路,对工程落地更重要。
这篇文章适合三类读者:正在做 RAG 应用的开发者,想评估自己 Agent 链路是否合格的算法工程师,以及准备搭建评测集、为模型选型提供依据的技术负责人。
2. VAKRA 的核心概念与适用场景
把标题拆开看,VAKRA 评测的不是一个单点能力,而是四个能力的组合。先逐个理解。
| 概念 | 通俗解释 | 评测关注点 |
|---|---|---|
| Multi-Hop Reasoning | 需要多步推理才能回答的问题 | 中间信息的顺序、依赖关系、跨文档整合 |
| APIs | 模型可以调用的外部函数或接口 | 工具选择、参数格式、返回结果解析 |
| Retrieval | 从外部知识库中搜索相关片段 | 检索时机、检索词质量、检索结果筛选 |
| Tool-Use Policies | 支配工具调用行为的一组规则 | 调用次数、调用顺序、白名单、安全约束 |
Multi-Hop Reasoning 不是简单的“问一个问题答一个答案”。它要求模型把多个信息片段串联起来。这种串联有两种常见模式:顺序多跳和并行多跳。顺序多跳是第 N 步依赖第 N-1 步的结果,比如先查营收再算毛利率;并行多跳是多个独立信息可以同时取回,比如同时检索两家公司的财报,最后再比较。VAKRA 这类评测通常两种都会覆盖。
Retrieval 在这里不是独立步骤,而是推理链的一部分。模型需要自己决定“我现在缺什么信息”“我用什么关键词去检索”“检索回来的片段里哪部分是可信的”。传统 RAG 评测只关心片段是否相关,但 VAKRA 评测更关心模型能不能把检索结果正确拼进推理链。检索时机太早或太晚,都会导致链路质量下降。
Tool-Use Policies 是最容易被忽略、也最容易出问题的部分。策略不是提示词里的“请谨慎调用工具”,而是可执行的规则:允许调用哪些工具、禁止哪些参数值、最大调用次数、敏感词过滤、超时处理、必须要调用的工具列表。很多 Agent 在 demo 里跑得很好,一到生产环境就失控,根本原因是策略定义不完整。比如没有限制最大调用次数,模型陷入循环;没有校验参数,模型把过期日期传进去了;没有黑名单,模型把“内部资料”放进了检索词。
从场景上看,VAKRA 这类评测最适配的是需要“决策 - 查询 - 再决策”的复杂任务,比如金融分析、医疗问答、运维诊断、科研文献综述。像“今天天气怎么样”这种单次 API 调用场景,用不上那么多跳;而“帮我诊断一下这个服务为什么慢”这类故障排查场景,天然就是多跳推理加多工具调用的组合。
3. 从任务设计看 VAKRA 的评测逻辑
理解一个评测基准,最好的方式是看它的任务设计。一个完整的 VAKRA 风格任务,至少包含以下要素:
- question:一个需要多步推理才能回答的问题。
- retrieval_targets:可能要用到的知识库或文档集合。
- tools:允许模型调用的 API 列表,每个工具要有名称、描述、参数说明。
- policy:工具使用策略,包括调用上限、白名单、必调工具、禁止词、输出格式。
- expected_chain:期望的调用链或判分规则。
下面是一个任务定义的示例,它模拟了一个需要“查财报 + 算指标 + 检索行业均值”的多跳问题。
{ "task_id": "t001", "question": "根据诺德公司的财报数据和已知的行业平均毛利率(42%),判断诺德公司2024年毛利率是否高于行业平均。", "retrieval_targets": ["诺德公司2024年年报", "锐新行业研究报告"], "tools": [ { "name": "lookup_financial_data", "description": "查询公司财报中的指定指标", "parameters": ["company", "metric"] }, { "name": "calc_gross_margin", "description": "根据营收和营业成本计算毛利率", "parameters": ["revenue", "cost"] }, { "name": "search_document", "description": "从知识库检索相关文档片段", "parameters": ["query"] } ], "policy": { "max_tool_calls": 4, "must_use": ["lookup_financial_data"], "forbidden_keywords": ["公司内部资料"], "answer_format": "简短结论,最多100字" }, "expected_chain": [ "lookup_financial_data", "calc_gross_margin", "search_document" ] }这个 JSON 看起来简单,但已经把评测逻辑讲清楚了。
答案对错只是其中一项。VAKRA 更关注模型的过程是否符合预期。在 expected_chain 中,第一步必须是 lookup_financial_data,而不是 search_document。因为题目明确说“财报数据”和“行业平均毛利率”已知,正确的方式是先取财报,而不是先搜文档。模型如果在第一步就选择检索,说明它对工具职责的理解是混乱的。
policy 里的 max_tool_calls 是防呆设计。如果模型反复调用同一个工具,调用次数超过 4 次,评测系统就会判失败,并认为模型的规划能力不足。must_use 保证了模型不能偷懒跳过关键查询。forbidden_keywords 则模拟了安全约束:任何包含敏感词的输入都不允许进入工具调用参数,这是工具使用策略中常见的安全边界。
这种设计带来的最大变化是错误定位的粒度。传统评测只会告诉你“这道题错了”,VAKRA 风格的任务设计可以告诉你“错在第三跳,该用计算工具却用了检索工具”。对于 Agent 开发链路,这种定位能力极其宝贵。
4. 环境准备与最小评测实践
如果你手上暂时没有 VAKRA 的官方代码仓库,完全可以先按它的思路搭建一个最小评测环境。这里我们用 Python 模拟一个不依赖真实外部 API 的演示系统,目的是把多跳推理和工具策略的流程完整跑通。
环境方面,建议 Python 3.9 以上版本,无需额外框架。如果后续要接入真实 LLM,再根据模型厂商的 SDK 调整。这里展示的是评测流程本身,所以用了确定性规则代替大模型决策,方便读者对照理解每一步。
# 创建项目目录 mkdir vakra-demo cd vakra-demo # 创建虚拟环境 python -m venv venv source venv/bin/activate # Windows 下用 venv\Scripts\activate # 无第三方依赖,标准库即可运行 python --version先定义一个模拟的工具注册表。这里把三个工具写成普通函数,并放在一个字典里,模拟真实 Agent 的工具注册机制。
# 文件路径:vakra-demo/tool_registry.py import re from typing import Callable, Dict def search_document(query: str) -> str: """模拟知识库检索,返回固定文档片段。""" docs = { "行业平均毛利率": "锐新行业研究报告显示,2024年行业平均毛利率为42%。", "诺德公司营收": "诺德公司2024年报显示,营业收入为58.6亿元。", "诺德公司营业成本": "诺德公司2024年报显示,营业成本为44.1亿元。", } return docs.get(query, "未检索到相关信息") def calc_gross_margin(revenue: float, cost: float) -> float: """计算毛利率。""" return round((revenue - cost) / revenue * 100, 2) def lookup_financial_data(company: str, metric: str) -> str: """查询财报指标,模拟 API 返回。""" data = { ("诺德公司", "营收"): "58.6亿元", ("诺德公司", "营业成本"): "44.1亿元", } return data.get((company, metric), "无数据") TOOL_REGISTRY: Dict[str, Callable] = { "search_document": search_document, "calc_gross_margin": calc_gross_margin, "lookup_financial_data": lookup_financial_data, }这里真正容易踩坑的地方是工具参数的类型。calc_gross_margin 的入参是浮点数,而 lookup_financial_data 返回的是字符串。真实项目里,如果模型把“58.6亿元”原样传给浮点参数,工具调用会直接报错。所以工具层需要做解析和容错,不能把字符串裸传给数值计算函数。
下面定义策略类。策略是 Agent 调用工具前的守门人,每一次调用都要经过它允许。
# 文件路径:vakra-demo/policy.py from typing import Dict, List class ToolPolicy: def __init__( self, tool_registry: Dict, max_tool_calls: int = 4, forbidden_keywords: List[str] = None ): self.tool_registry = tool_registry self.max_tool_calls = max_tool_calls self.forbidden_keywords = forbidden_keywords or [] self.call_count = 0 def check(self, tool_name: str, args: Dict) -> bool: if self.call_count >= self.max_tool_calls: print( f"[policy] 达到最大调用次数 {self.max_tool_calls}," f"拒绝调用 {tool_name}" ) return False if tool_name not in self.tool_registry: print(f"[policy] 工具 {tool_name} 不在白名单,拒绝调用") return False for key, value in args.items(): if any(kw in str(value) for kw in self.forbidden_keywords): print(f"[policy] 参数 {key}={value} 包含敏感词,拒绝调用") return False self.call_count += 1 print( f"[policy] 允许调用 {tool_name},累计 {self.call_count}/{self.max_tool_calls}" ) return True这个策略类实现了三个常见约束:最大调用次数、工具白名单、敏感词过滤。在真实评测中,还可以扩展出“必须调用的工具”“禁止连续调用同一工具”“单次调用超时”等规则。策略越细,越能测出 Agent 在复杂约束下的表现。
5. 核心流程拆解:一个多跳工具调用示例
现在把上面的模块组装成一个可运行的 Agent 主流程。这个流程明确拆成四跳:两跳查数据、一跳算指标、一跳检索行业数据。
# 文件路径:vakra-demo/agent_demo.py import re from tool_registry import TOOL_REGISTRY from policy import ToolPolicy def agent_loop(question: str) -> str: policy = ToolPolicy( tool_registry=TOOL_REGISTRY, max_tool_calls=4, forbidden_keywords=["公司内部资料"] ) revenue = 0.0 cost = 0.0 gross_margin = None industry_avg = None print(f"[agent] 开始处理问题:{question}\n") # 第一跳:查询营收 if policy.check( "lookup_financial_data", {"company": "诺德公司", "metric": "营收"} ): result = lookup_financial_data("诺德公司", "营收") revenue = float(re.search(r"(\d+\.?\d*)亿元", result).group(1)) print(f"[agent] 第1跳结果:营收 = {result}") # 第二跳:查询营业成本 if policy.check( "lookup_financial_data", {"company": "诺德公司", "metric": "营业成本"} ): result = lookup_financial_data("诺德公司", "营业成本") cost = float(re.search(r"(\d+\.?\d*)亿元", result).group(1)) print(f"[agent] 第2跳结果:营业成本 = {result}") # 第三跳:计算毛利率 if policy.check( "calc_gross_margin", {"revenue": revenue, "cost": cost} ): gross_margin = calc_gross_margin(revenue, cost) print(f"[agent] 第3跳结果:诺德公司毛利率 = {gross_margin}%") # 第四跳:检索行业平均毛利率 if policy.check( "search_document", {"query": "行业平均毛利率"} ): industry_doc = search_document("行业平均毛利率") industry_avg = float(re.search(r"(\d+\.?\d*)%", industry_doc).group(1)) print(f"[agent] 第4跳结果:行业平均毛利率 = {industry_avg}%") print() if gross_margin is not None and industry_avg is not None: comparison = "高于" if gross_margin > industry_avg else "低于" conclusion = ( f"诺德公司2024年毛利率为{gross_margin}%," f"{comparison}行业平均毛利率{industry_avg}%。" ) else: conclusion = "信息不完整,无法得出可靠结论。" print(f"[agent] 最终结论:{conclusion}") return conclusion if __name__ == "__main__": agent_loop( "根据诺德公司的财报数据和已知的行业平均毛利率," "判断诺德公司2024年毛利率是否高于行业平均。" )这个示例的每一步都有明确意图。第一跳和第二跳是基础数据准备,第三跳是推理计算,第四跳是补充外部信息。前三跳如果失败,比如营收没查到,后面的计算和比较就无从谈起,这就是顺序多跳依赖的典型表现。
有一点要特别说明:这里用正则解析工具返回结果,在真实项目里是一个需要谨慎设计的环节。工具返回的数据往往有不同格式,可能是 JSON、CSV 或纯文本。Agent 的开发框架通常会把结构化输出解析成字典,而不是让模型直接处理字符串。这里为了展示链路逻辑,故意简化了解析过程。实际生产环境建议工具统一返回 JSON,并在工具层完成类型转换。
从策略角度看,这个 Agent 的调用次数是 4 次,正好踩在 max_tool_calls=4 的上限。如果哪个环节需要多一次重试,策略就会拒绝下一次调用,最终结论会变成“信息不完整”。在真实评测里,这种边界情况恰恰是 VAKRA 想测的:模型在资源受限时,是选择继续消耗额外资源,还是基于已有信息给出带条件的回答。
6. 运行结果与效果验证
直接把 demo 跑起来,看完整输出。
python agent_demo.py预期输出如下:
[agent] 开始处理问题:根据诺德公司的财报数据和已知的行业平均毛利率,判断诺德公司2024年毛利率是否高于行业平均。 [policy] 允许调用 lookup_financial_data,累计 1/4 [agent] 第1跳结果:营收 = 58.6亿元 [policy] 允许调用 lookup_financial_data,累计 2/4 [agent] 第2跳结果:营业成本 = 44.1亿元 [policy] 允许调用 calc_gross_margin,累计 3/4 [agent] 第3跳结果:诺德公司毛利率 = 24.74% [policy] 允许调用 search_document,累计 4/4 [agent] 第4跳结果:行业平均毛利率 = 42.0% [agent] 最终结论:诺德公司2024年毛利率为24.74%,低于行业平均毛利率42.0%。如何判断运行成功?看三个信号。
第一,策略日志里展示了 4 次调用都通过了白名单和敏感词检查,没有出现“拒绝调用”的拦截。第二,Agent 日志的每一跳结果都符合预期,营收、成本、毛利率、行业均值的数据没有明显异常。第三,最终结论基于完整链路得出,并且正确判断出“低于行业平均”。
如果输出不符合预期,第一步应该看策略日志。如果出现“拒绝调用”,说明 Agent 的调用次数或参数触发了策略限制。如果缺少某一跳的日志,比如没有第3跳,说明上一个条件判断失败,需要检查正则解析是否匹配到内容。
把这个流程对应到 VAKRA 评测里,一个合格的 Agent 评测结果应该同时满足两个维度:最终答案正确,且调用链与 expected_chain 一致。在上面这个示例里,调用链是 lookup_financial_data -> lookup_financial_data -> calc_gross_margin -> search_document,与 expected 的“查数据 -> 算指标 -> 检索行业”语义一致,评测就通过。如果模型一上来调用 search_document,哪怕最后结论碰巧对了,评测也会判定为过程不合格。
7. 常见问题与排查方法
在构建类似评测链路时,下面几类问题出现频率最高。
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
| 模型跳过检索直接回答 | 提示词没有强调必须调用工具,或评测任务设计时未设置 must_use 策略 | 查看模型输出前缀,确认是否生成了工具调用 | 在 policy 中增加 must_use 配置,对未调用工具的结果直接判失败 |
| 重复调用同一个工具 | 模型没有记忆前序调用结果,或上下文被截断 | 检查工具调用历史是否完整传入模型上下文 | 增加上下文管理,记录已完成的工具调用和返回结果 |
| 上下文被检索片段撑爆 | 检索返回多个长文档,导致超过模型窗口长度 | 观察 token 使用量和报错信息 | 设置检索片段的长度上限,对结果做摘要或截断 |
| 工具参数格式错误 | 工具定义缺少参数类型说明,或模型未按 schema 输出 | 查看工具调用日志中的参数值 | 工具定义补充参数类型和示例,并在工具层做参数校验 |
| 策略约束未生效 | 策略代码没有挂在 Agent 主循环上,只写在系统提示词里 | 打印策略 check 的调用日志 | 把策略检查前置到工具分发层,任何调用都必须经过策略检查 |
| 工具调用报认证或密钥错误 | 外部 API 的认证配置不正确,常见于数据库连接或云服务接口 | 查看异常堆栈和连接配置 | 检查连接串的 SSL 与密钥参数。以 MySQL 为例,报错 Public Key Retrieval is not allowed 时,通常是因为驱动默认不允许向服务器请求公钥,需要确认驱动版本、连接参数和安全策略后决定如何配置 |
最后一项值得多说几句。很多 Agent 调用外部工具时,问题不是模型不会选工具,而是工具本身没配好。比如访问数据库时需要做公钥交换,但安全策略禁用了自动获取公钥,于是工具调用一直失败。这类问题表面上和模型能力无关,但会直接影响评测结果。在搭建 VAKRA 风格评测时,建议先把工具层的连通性验证跑通,再让模型去调用,否则很容易把工具故障误判成模型能力不足。
8. 工程实践与 Agent 设计建议
评测框架最终要服务于生产。基于 VAKRA 的评测思路,下面这些工程经验值得借鉴。
8.1 工具定义要窄而明确
不要给 Agent 一个“万能工具”。工具越宽泛,模型误用的概率越高。比如把“get_company_financial_data(company, metric)”设计成一个限定公司和指标的窄接口,比给一个“execute_sql_query(sql)”的万能接口可靠得多。万能接口虽然灵活,但很容易让模型生成有安全风险的 SQL。VAKRA 评测中,工具语义清晰度会直接反映在策略合规率上。
8.2 策略检查必须在工具分发层,而不是提示词层
提示词里的“请谨慎调用”是软约束,模型可能不遵守。策略代码必须硬编码在工具分发路径上,任何工具调用都要经过白名单、次数上限、参数校验这三道关卡。这样即使模型规划出错,系统也能兜底,保证评测和生产环境的安全边界。
8.3 建立多跳追踪日志
每一步工具调用都要留下结构化日志,内容包括:调用顺序、参数、返回结果、耗时、策略是否放行。日志是用来定位链路问题的唯一依据。没有日志,评测中发现某道题答错,你只能猜是模型的问题还是检索的问题。有了日志,一眼就能看出是哪一跳出了错。
{ "trace_id": "a01f8c", "step": 3, "tool": "calc_gross_margin", "args": {"revenue": 58.6, "cost": 44.1}, "result": 24.74, "policy_allowed": true }8.4 评测集要包含负样本
很多人建评测集只收集“预期链路正确”的样本,这远远不够。真正有价值的评测集,至少要包含三类负样本:应当调用工具但模型没有调用的,应当按顺序调用但模型跳步的,以及应当拒绝调用但模型强行调用的。VAKRA 强调工具使用策略,本质上就是要把“合规性”纳入评测。没有负样本,你无法测出策略约束有没有生效。
8.5 安全与最小权限原则
给 Agent 的工具权限遵循最小权限原则。评测环境可以用完整数据集,但生产环境只授予满足任务的最小范围。比如只读数据库账号、IP 白名单、敏感字段脱敏。工具策略里的 forbidden_keywords 就是安全边界的一种体现。真实项目里,要结合公司安全规范定义哪些内容不能进入模型上下文,哪些字段不允许被工具读取。
8.6 控制成本和资源上限
多跳推理的代价是多次 API 调用。评测时如果不限制调用次数,一个难一点的题目可能触发几十次调用,成本和耗时都会失控。建议设置与任务复杂度匹配的 max_tool_calls,并在评测结果里记录“实际调用次数/允许调用次数”这一指标,用它考察模型的路径效率。
9. 总结与后续学习方向
VAKRA 这类评测真正改变的不是题目难度,而是评测视角:从“模型会不会说话”转向“模型会不会干活、懂不懂规矩”。一个能正确回答百科知识但盲目调用工具、不遵守调用策略的 Agent,在生产环境里反而是隐患。通过把 Multi-Hop Reasoning、APIs、Retrieval、Tool-Use Policies 放在同一条评测链路上,团队可以更快定位 Agent 的能力短板,而不是把时间浪费在猜错因上。
如果你正在搭建自己的 Agent 评测体系,我建议先做三件事:一是按第 3 节的任务格式,整理出十条覆盖多跳推理和策略约束的评测样例;二是用第 5 节的最小示例跑通链路,确认策略检查和工具分发没有漏洞;三是把日志规范提前定义好,保证每一跳都可追溯。等这套最小框架稳定后,再接入真实模型、真实 API,逐步替换模拟数据。
后续值得继续深入的方向包括:更精细的调用策略评测、Agent 错误恢复能力评估、以及如何在评测中引入自动化裁判。但无论工具链怎么演进,核心思路不变:Agent 的价值不在于它能调用多少工具,而在于它能不能在正确的约束下,把工具用对、把链路走完。