主题
本教程演示如何开始用 LlamaIndex 构建智能体(agent)。我们先从一个最基础的示例入手,再说明如何加入 RAG(检索增强生成)能力。
我们用 BAAI/bge-base-en-v1.5 作为嵌入模型,用通过 Ollama 提供的 llama3.1 8B 作为 LLM。
提示
请先完成安装这一步。
设置
Ollama 是一个帮你以最少的配置在本地跑起 LLM 的工具。
安装方法请参照它的 README。
要下载 Llama3 模型,只需执行 ollama pull llama3.1。
注意:你需要一台至少 ~32GB 内存的机器。
正如安装指南所说,llama-index 其实是一组包的集合。要使用 Ollama 和 Huggingface,需要先安装这些集成:
终端窗口
pip install llama-index-llms-ollama llama-index-embeddings-huggingface这些包名和导入路径是一致的,记起来非常方便,无论是要导入还是安装!
from llama_index.llms.ollama import Ollama
from llama_index.embeddings.huggingface import HuggingFaceEmbedding更多集成列在集成页面上。
基础的 Agent 示例
先来看一个简单的例子:一个能调用工具做基础乘法的 agent。新建一个名为 starter.py 的文件:
import asyncio
from llama_index.core.agent.workflow import FunctionAgent
from llama_index.llms.ollama import Ollama
# Define a simple calculator tool
def multiply(a: float, b: float) -> float:
"""Useful for multiplying two numbers."""
return a * b
# Create an agent workflow with our calculator tool
agent = FunctionAgent(
tools=[multiply],
llm=Ollama(
model="llama3.1",
request_timeout=360.0,
# Manually set the context window to limit memory usage
context_window=8000,
),
system_prompt="You are a helpful assistant that can multiply two numbers.",
)
async def main():
# Run the agent
response = await agent.run("What is 1234 * 4567?")
print(str(response))
# Run the agent
if __name__ == "__main__":
asyncio.run(main())它会输出类似这样的内容:The answer to 1234 * 4567 is 5635678.
刚才发生的事是:
- 我们给 agent 提了一个问题:
What is 1234 * 4567? - 底层会把这个问题,连同工具的 schema(名称、docstring 与参数)一起传给 LLM
- Agent 选中了
multiply工具,并写好了传给工具的参数 - Agent 拿到工具的返回结果,把它整合进最终回答
提示
如你所见,我们用的是 async 的 Python 函数。许多 LLM 和模型都支持异步调用,为了提升应用性能,推荐使用异步代码。想更多了解异步编程与 Python,建议读一读这篇异步 + Python 简述。
加入聊天历史
AgentWorkflow 还能记住之前的消息。这些记忆保存在 AgentWorkflow 的 Context 里。
如果传入了 Context,agent 会用它继续这段对话。
from llama_index.core.workflow import Context
# create context
ctx = Context(agent)
# run agent with context
response = await agent.run("My name is Logan", ctx=ctx)
response = await agent.run("What is my name?", ctx=ctx)加入 RAG 能力
接下来通过给 agent 加上检索文档的能力来做增强。先在终端里准备一些示例数据:
终端窗口
mkdir data
wget https://raw.githubusercontent.com/run-llama/llama_index/main/docs/examples/data/paul_graham/paul_graham_essay.txt -O data/paul_graham_essay.txt此时你的目录结构应该像这样:
├── starter.py └── data └── paul_graham_essay.txt现在我们可以用 LlamaIndex 做一个检索文档的工具。我们不依赖任何托管 API 的默认配置,而是把 Settings.embed_model 配成 HuggingFaceEmbedding(model_name="BAAI/bge-base-en-v1.5"),让索引和检索都使用本地的 Sentence Transformers 模型,无需任何 API key。
改动后的 starter.py 应该像这样:
from llama_index.core import VectorStoreIndex, SimpleDirectoryReader, Settings
from llama_index.core.agent.workflow import AgentWorkflow
from llama_index.llms.ollama import Ollama
from llama_index.embeddings.huggingface import HuggingFaceEmbedding
import asyncio
import os
# Settings control global defaults
Settings.embed_model = HuggingFaceEmbedding(model_name="BAAI/bge-base-en-v1.5")
Settings.llm = Ollama(
model="llama3.1",
request_timeout=360.0,
# Manually set the context window to limit memory usage
context_window=8000,
)
# Create a RAG tool using LlamaIndex
documents = SimpleDirectoryReader("data").load_data()
index = VectorStoreIndex.from_documents(
documents,
# we can optionally override the embed_model here
# embed_model=Settings.embed_model,
)
query_engine = index.as_query_engine(
# we can optionally override the llm here
# llm=Settings.llm,
)
def multiply(a: float, b: float) -> float:
"""Useful for multiplying two numbers."""
return a * b
async def search_documents(query: str) -> str:
"""Useful for answering natural language questions about an personal essay written by Paul Graham."""
response = await query_engine.aquery(query)
return str(response)
# Create an enhanced workflow with both tools
agent = AgentWorkflow.from_tools_or_functions(
[multiply, search_documents],
llm=Settings.llm,
system_prompt="""You are a helpful assistant that can perform calculations
and search through documents to answer questions.""",
)
# Now we can ask questions about the documents or do calculations
async def main():
response = await agent.run(
"What did the author do in college? Also, what's 7 * 8?"
)
print(response)
# Run the agent
if __name__ == "__main__":
asyncio.run(main())现在这个 agent 已经能在计算器和文档检索之间自如切换来回答问题了。
持久化 RAG 索引
为了避免每次都重新处理文档,你可以把索引持久化到磁盘:
# Save the index
index.storage_context.persist("storage")
# Later, load the index
from llama_index.core import StorageContext, load_index_from_storage
storage_context = StorageContext.from_defaults(persist_dir="storage")
index = load_index_from_storage(
storage_context,
# we can optionally override the embed_model here
# it's important to use the same embed_model as the one used to build the index
# embed_model=Settings.embed_model,
)
query_engine = index.as_query_engine(
# we can optionally override the llm here
# llm=Settings.llm,
)提示
如果你用了默认之外的向量库集成,通常可以直接从那个向量库重新加载:
index = VectorStoreIndex.from_vector_store(
vector_store,
# it's important to use the same embed_model as the one used to build the index
# embed_model=Settings.embed_model,
)下一步做什么?
这还只是 LlamaIndex agent 能力的开端。你可以:
- 给 agent 加入更多工具
- 换用不同的 LLM
- 用系统提示词定制 agent 的行为
- 加入流式输出能力
- 实现在环(human-in-the-loop)工作流
- 让多个 agent 协作完成任务
几个有用的下一步链接: