跳转到内容

05 - 工具调用与 Function Calling

05 - 工具调用与 Function Calling

学习目标

本章将带你深入 LlamaIndex 的工具抽象层。你将学到:

  • BaseTool / AsyncBaseTool 类型体系
  • FunctionTool.from_defaults() 签名推断机制
  • requires_context 的 Context 自动注入
  • BaseToolSpec 工具集合模式
  • QueryEngineTool 将检索引擎包装为工具
  • 同步工具的异步适配

前置知识


项目实践

工具类型体系

类型层级

  • BaseTool:抽象基类,定义 __call__ 接口
  • AsyncBaseTool:扩展 BaseTool,同时提供同步 call 和异步 acall
  • BaseToolAsyncAdapter:将同步 BaseTool 包装为异步(通过 asyncio.to_thread()
  • FunctionTool:包装普通 Python 函数
  • QueryEngineTool:包装 QueryEngine

FunctionTool.from_defaults() 签名推断

一行代码将普通函数注册为工具:

def search_knowledge_base(query: str, top_k: int = 5, source: str = "all") -> str:
"""在知识库中搜索相关内容。
Args:
query: 搜索查询
top_k: 返回结果数量
source: 数据来源 (all/docs/notes)
"""
return do_search(query, top_k, source)
tool = FunctionTool.from_defaults(fn=search_knowledge_base)

自动推断结果

tool.metadata.name == "search_knowledge_base"
tool.metadata.description == "在知识库中搜索相关内容。..." # 来自 docstring
tool.metadata.fn_schema # Pydantic 模型: {query: str, top_k: int, source: str}

推断流程

  1. 从函数名提取工具名
  2. 从 docstring 提取工具描述
  3. 从参数签名使用 Pydantic 推断 fn_schema(JSON Schema)

requires_context:Context 自动注入

当函数签名中包含 Context 类型的参数时,Agent 在调用工具时会自动注入当前工作流上下文,无需 LLM 提供:

from llama_index.core.workflow import Context
def search_with_state(ctx: Context, query: str) -> str:
"""搜索并携带当前状态。"""
current_memory = ctx.data.get("memory", [])
return do_search(query, context=current_memory)
tool = FunctionTool.from_defaults(fn=search_with_state)
# Agent 调用时自动注入 ctx 参数

设计优势:将”工具需要的运行时信息”与”LLM 提供的参数”分离。LLM 只需要提供 queryctx 由 Agent 框架自动填充。

BaseToolSpec 工具集合模式

一个类可以定义多个相关工具方法,通过 spec_functions 列表声明哪些方法作为工具暴露:

class DataAnalysisSpec(BaseToolSpec):
spec_functions = ["calculate_mean", "calculate_median", "calculate_correlation"]
def calculate_mean(self, data: list[float]) -> float:
"""计算平均值。"""
return sum(data) / len(data)
def calculate_median(self, data: list[float]) -> float:
"""计算中位数。"""
...
def calculate_correlation(self, x: list[float], y: list[float]) -> float:
"""计算相关系数。"""
...
# 批量转换为 FunctionTool 列表
tools = DataAnalysisSpec().to_tool_list() # 返回 3 个 FunctionTool

适用场景:一组功能紧密相关的工具(如数据分析、搜索引擎、文件操作),通过一个类统一管理。

QueryEngineTool:检索引擎即工具

将 RAG 查询引擎包装为 Agent 可调用的工具:

from llama_index.core.tools import QueryEngineTool
tool = QueryEngineTool(
query_engine=vector_index.as_query_engine(),
metadata=ToolMetadata(
name="tech_docs_search",
description="搜索技术文档数据库",
),
)

设计优势:Agent 可以将 RAG 查询引擎作为工具调用,实现”Agent 内部的 RAG”——Agent 推理过程中按需检索知识库。

工具返回类型处理

LlamaIndex 的工具支持多种返回类型,统一转换为 ContentBlock 列表:

返回类型处理方式
str直接包装为 TextBlock
ImageBlock直接传递
Document / Node提取文本内容
List[ContentBlock]直接返回

问题与规避

陷阱对策
函数签名中无意包含 Context 导致 LLM 困惑只对真正需要运行时状态的工具使用 requires_context
docstring 过长导致工具描述超出上下文窗口在 docstring 开头用一句话概括核心功能
FunctionTool 参数类型不被 Pydantic 识别使用 fn_schema=MyCustomSchema 手动指定
同步工具阻塞事件循环使用 BaseToolAsyncAdapter 或确保工具函数是非阻塞的

设计取舍

自动签名推断 vs 手动 Schema 定义

维度FunctionTool.from_defaults()手动定义 ToolMetadata
代码量一行多行
灵活性低(依赖签名和 docstring)高(完全自定义)
描述精度中(docstring 全文作为描述)高(可以精确控制描述)
适用场景快速原型生产环境精细控制

参考来源