本案例展示了如何使用LlamaIndex构建一个基于Anthropic函数调用能力的智能代理。该代理能够使用预定义的工具执行特定任务,并通过函数调用机制与外部系统交互。
1. 案例目标
本案例的主要目标是演示如何使用LlamaIndex和Anthropic API构建一个具有函数调用能力的智能代理。具体来说,我们将:
- 创建一个基于Anthropic的FunctionAgent
- 定义简单的计算工具(加法和乘法)
- 演示代理如何使用工具执行数学计算
- 展示如何管理代理的上下文和记忆
- 构建一个基于RAG的代理,用于查询文档内容
2. 技术栈与核心依赖
- LlamaIndex- 用于构建智能代理和工作流
- Anthropic API- 提供Claude语言模型和函数调用功能
- OpenAI API- 提供文本嵌入模型
- FunctionAgent- 用于构建具有函数调用能力的代理
- VectorStoreIndex- 用于构建RAG系统
- QueryEngineTool- 用于将查询引擎封装为工具
核心依赖包
%pip install llama-index %pip install llama-index-llms-anthropic %pip install llama-index-embeddings-openai3. 环境配置
首先需要设置Anthropic API密钥:
from llama_index.llms.anthropic import Anthropic llm = Anthropic(model="claude-3-opus-20240229", api_key="sk-...")对于RAG部分,还需要设置OpenAI API密钥:
from llama_index.embeddings.openai import OpenAIEmbedding embed_model = OpenAIEmbedding( model_name="text-embedding-3-large", api_key="sk-proj-..." ) query_llm = Anthropic(model="claude-3-haiku-20240307", api_key="sk-...")4. 案例实现
4.1 工具定义
定义简单的计算工具:
def multiply(a: int, b: int) -> int: """Multiple two integers and returns the result integer""" return a * b def add(a: int, b: int) -> int: """Add two integers and returns the result integer""" return a + b4.2 初始化Anthropic代理
使用定义好的工具创建FunctionAgent:
from llama_index.core.agent.workflow import FunctionAgent agent = FunctionAgent( tools=[multiply, add], llm=llm, )4.3 代理交互
创建一个函数来运行代理并显示详细的工具调用信息:
from llama_index.core.agent.workflow import ToolCallResult async def run_agent_verbose(query: str): handler = agent.run(query) async for event in handler.stream_events(): if isinstance(event, ToolCallResult): print( f"Called tool {event.tool_name} with args {event.tool_kwargs}\nGot result: {event.tool_output}" ) return await handler4.4 执行数学计算
使用代理执行数学计算:
response = await run_agent_verbose("What is (121 + 2) * 5?") print(str(response))输出示例:
Called tool add with args {'a': 121, 'b': 2} Got result: 123 Called tool multiply with args {'a': 123, 'b': 5} Got result: 615 Therefore, (121 + 2) * 5 = 6154.5 管理上下文/记忆
默认情况下,`.run()`是无状态的。如果要维护状态,可以传入一个`context`对象:
from llama_index.core.workflow import Context ctx = Context(agent) response = await agent.run("My name is John Doe", ctx=ctx) response = await agent.run("What is my name?", ctx=ctx) print(str(response))4.6 构建基于RAG的代理
4.6.1 准备数据
!mkdir -p 'data/10k/' !wget 'https://raw.githubusercontent.com/run-llama/llama_index/main/docs/examples/data/10k/uber_2021.pdf' -O 'data/10k/uber_2021.pdf'4.6.2 构建查询引擎
from llama_index.core.tools import QueryEngineTool from llama_index.core import SimpleDirectoryReader, VectorStoreIndex # 加载数据 uber_docs = SimpleDirectoryReader( input_files=["./data/10k/uber_2021.pdf"] ).load_data() # 构建索引 uber_index = VectorStoreIndex.from_documents( uber_docs, embed_model=embed_model ) uber_engine = uber_index.as_query_engine(similarity_top_k=3, llm=query_llm) query_engine_tool = QueryEngineTool.from_defaults( query_engine=uber_engine, name="uber_10k", description=( "Provides information about Uber financials for year 2021. " "Use a detailed plain text question as input to the tool." ), )4.6.3 创建基于RAG的代理
from llama_index.core.agent.workflow import FunctionAgent agent = FunctionAgent(tools=[query_engine_tool], llm=llm, verbose=True)4.6.4 查询文档内容
response = await agent.run( "Tell me both the risk factors and tailwinds for Uber?" ) print(str(response))5. 案例效果
5.1 数学计算能力
代理成功使用定义的加法和乘法工具执行了复杂的数学计算。具体来说:
- 代理正确解析了数学表达式"(121 + 2) * 5"
- 代理首先调用加法工具计算121 + 2 = 123
- 然后调用乘法工具计算123 * 5 = 615
- 最后给出了完整的计算过程和结果
5.2 上下文记忆能力
通过Context对象,代理能够记住之前的对话内容。当用户询问"我的名字是什么?"时,代理能够正确回答之前提到的"John Doe"。这展示了代理的状态管理能力。
5.3 文档查询能力
基于RAG的代理成功查询了Uber 2021年的10-K文档,并提取了关于风险因素和增长机会的信息。具体来说:
- 代理正确识别了需要使用查询工具来回答问题
- 代理将自然语言问题转换为适合查询引擎的格式
- 代理从查询结果中提取了关键信息,并进行了总结
- 代理提供了关于Uber风险因素和增长机会的详细回答
5.4 工具调用透明度
通过流式事件处理,用户可以清楚地看到代理调用了哪些工具、传递了什么参数以及获得了什么结果。这种透明度使得代理的行为更加可理解和可调试。
6. 案例实现思路
6.1 函数调用机制
本案例基于Anthropic的函数调用机制,允许语言模型调用预定义的函数。这种机制使得代理能够与外部系统交互,执行超出语言模型本身能力的任务,如数学计算、数据查询等。
6.2 工具定义与封装
通过定义Python函数并将其作为工具传递给FunctionAgent,我们实现了工具的封装。每个工具都有明确的参数类型和返回类型,以及描述其功能的文档字符串。这种设计使得代理能够理解工具的用途并正确调用它们。
6.3 代理与工具的交互
代理通过函数调用机制与工具交互。当用户提出请求时,代理会分析请求并决定是否需要调用工具。如果需要,代理会生成适当的函数调用,包括函数名和参数。工具执行后,代理会处理结果并生成最终回答。
6.4 状态管理
通过Context对象,我们实现了代理的状态管理。Context对象允许在多次调用之间共享状态,使代理能够记住之前的对话内容。这种状态管理对于构建有记忆的对话系统至关重要。
6.5 RAG集成
通过将RAG查询引擎封装为QueryEngineTool,我们实现了代理与RAG系统的集成。代理可以使用查询工具从文档中检索信息,从而回答基于特定文档的问题。这种扩展使得代理能够访问和利用外部知识库。
7. 扩展建议
7.1 增强工具能力
- 添加更多类型的工具,如数据库查询、API调用、文件操作等
- 实现工具的异步执行,提高并发性能
- 添加工具权限管理,确保代理只能调用授权的工具
7.2 改进代理能力
- 实现多轮对话中的工具链式调用,使代理能够解决更复杂的问题
- 添加工具调用失败时的重试机制和错误处理
- 实现工具调用的并行执行,提高效率
7.3 增强RAG功能
- 集成多模态RAG,支持图像、音频等多种数据类型
- 实现实时文档更新机制,保持RAG系统的知识新鲜度
- 添加文档来源引用,提高回答的可信度和可验证性
7.4 优化用户体验
- 实现更丰富的流式输出,包括中间步骤和思考过程
- 添加可视化工具调用流程的界面
- 实现用户反馈机制,允许用户纠正代理的错误
7.5 部署与扩展
- 设计分布式代理架构,支持大规模并发请求
- 实现代理的容器化部署,提高可移植性和可扩展性
- 添加监控和日志系统,跟踪代理性能和使用情况
8. 总结
本案例展示了如何使用LlamaIndex和Anthropic API构建一个具有函数调用能力的智能代理。通过这个案例,我们了解了:
- 如何定义工具并将其集成到代理中
- 如何使用FunctionAgent创建具有函数调用能力的代理
- 如何管理代理的上下文和记忆
- 如何将RAG系统与代理集成,实现基于文档的问答
- 如何通过流式事件处理监控代理的行为
函数调用机制是构建智能代理的关键技术之一,它使得代理能够与外部系统交互,执行超出语言模型本身能力的任务。通过将各种工具(如计算工具、查询工具、API工具等)集成到代理中,我们可以构建出功能强大的智能系统,解决各种复杂问题。
本案例中的代理虽然简单,但展示了构建智能代理的核心概念和技术。通过扩展工具集、改进代理能力和优化用户体验,我们可以构建出更加智能、更加实用的代理系统,为各行各业的数字化转型提供支持。