Featured image of post 如何用 LlamaIndex 实现 agent

如何用 LlamaIndex 实现 agent

agent 是什么?

打开市面上流行的AIGC应用,agent 几乎是必备的功能。agent到底是什么呢?

我们先从字面意思理解,看看各家公司做的产品是如何定义 agent 的。

智谱清言 (https://chatglm.cn) 将 agent 定义为 “智能体”Image

通义千问 (https://tongyi.aliyun.com/)将 agent 定义为 “智能体”Image

Kimi (https://kimi.moonshot.cn/)将 agent 定义为 “私人助理”Image

扣子(https://www.coze.cn/) 将 agent 定义为 “智能体”

Image

文心一言 (https://yiyan.baidu.com/) 将 agent 定义为 “智能体” (无图,拿来凑数)

agent 从英文直译过来是 “代理”,但从国内各AIGIC的产品定义来看,绝大多数公司将 agent 定义为了 “智能体”

好,我们再进一步,那什么是智能体呢?

从 “智能体” 使用者的角度看,“智能体” 就像专门完成某一类任务的机器人。智能体的设计目的其实就是为了实现特定的目标或完成特定的任务。

它像是一个专家一样,专门解决某一类的问题,用医生做比喻,agent就像一个专科大夫,比如骨科大夫,只能看骨科相关的病,而大模型LLM 像是一个全科大夫,什么病都能看。

从设计上不一样,使用上自然就要有所区别,比如你要处理和计算复杂的数学公式,那么向专门为解决这类问题而设计的 “智能体” 提问就比直接向LLM 提问得到的答案要准确。

agent 的创建和使用

我们以 智谱清言 举例,下图是在创建 “智能体” 时的设置页面内容,可以看到有不少的配置。这里面,我要说两个重要的配置。

Image

第一个重要的配置是:“配置信息

Image

看上图你是否觉得很熟悉,是的,如果你看过我之前的文章,就会注意到,这不就是 “prompt” 吗?准确地来说是 system promptrole prompt

所以,在我最初的认识里,agent 或者叫 “智能体” 简直就是一个 “prompt engineering” 的产物,只需要把 “提示词” 写好,就能创建好一个 agent 了。后来,我了解到,提示词工程确实很重要,但 agent 并不是只包括设置提示词 。

除了上图所示的 “联网能力”、“代码能力” 这些显而易见的功能外。另一个重要的配置就是 “知识库” 。加上知识库后,实际上这就是一个 RAG 应用了。这会让 agent 本身外挂一个我们自己上传的知识库,让它学习了我们上传的“知识” 后,再接受提问,就可以更准备地做出回答。

LlamaIndex

demo

用 LlamaIndex 实现一个简单的 agent demo 比较容易,看一下具体的代码实现:

 1import sys
 2import os
 3
 4sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..")))
 5
 6from llama_index.core.agent import ReActAgent
 7from llama_index.core.tools import FunctionTool
 8from llamaindex_demo.custom.custom_llm_glm import GLM4LLM
 9
10def multiply(a: float, b: float) -> float:
11    """Multiply two numbers and returns the product"""
12    return a * b
13
14def add(a: float, b: float) -> float:
15    """Add two numbers and returns the sum"""
16    return a + b
17
18def main():
19
20    multiply_tool = FunctionTool.from_defaults(fn=multiply)
21    add_tool = FunctionTool.from_defaults(fn=add)
22
23    # 使用GLM-4 Plus模型
24    llm = GLM4LLM()
25    # 创建ReActAgent实例
26    agent = ReActAgent.from_tools([multiply_tool, add_tool], llm=llm, verbose=True)
27
28    response = agent.chat("20+(2*4)等于多少?使用工具计算每一步")
29
30    print(response)
31
32if __name__ == "__main__":
33    main()

LLM 上,我们继续使用之前文章中使用过的 GLM。

我们看一下它的输出

Image

可以看出,它将提问中的计算步骤分别利用了我们自定义的函数 addmultiply ,而不是走大模型。挺有意思的吧,我们可以自定义 agent 中的某些处理流程。除了使用 prompt 外,我们的控制权更大了。

Rag demo

这个 demo 我们来看一下如何把rag 集成到 agent中。也很简单,我们直接上代码:

 1import os
 2import sys
 3import re
 4
 5sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..")))
 6
 7from llama_index.core.agent import ReActAgent
 8from llama_index.core.tools import FunctionTool
 9from llama_index.core.tools import QueryEngineTool
10from llama_index.core import VectorStoreIndex, SimpleDirectoryReader, Settings
11from llamaindex_demo import logger
12from llamaindex_demo.custom.custom_llm_glm import GLM4LLM
13from llamaindex_demo.custom.custom_embedding_zhipu import ZhipuEmbeddings
14from llama_parse import LlamaParse
15
16# 设置环境变量,禁用tokenizers的并行处理
17os.environ["TOKENIZERS_PARALLELISM"] = "false"
18
19def toHtml(text: str) -> str:
20    """遇到英文单词就用html标签包裹"""
21    return re.sub(r"(\b[a-zA-Z]+\b)", r'<span style="color:red;">\1</span>', text)
22
23def run_glm4_query_with_embeddings(query: str):
24    # 从指定目录加载文档数据
25    documents = SimpleDirectoryReader(input_files=["./data/sample.txt"]).load_data()
26
27    # 设置LLM和嵌入模型
28    Settings.llm = GLM4LLM()
29    Settings.embed_model = ZhipuEmbeddings()
30
31    # 创建索引和查询引擎 show_progress=True 显示 embedding 进度
32    index = VectorStoreIndex.from_documents(documents, show_progress=True)
33
34    query_engine = index.as_query_engine(streaming=True)
35
36    yc_tool = QueryEngineTool.from_defaults(
37        query_engine,
38        #name="YC 创始人的个人经理",
39        #description="关于YC创始人Paul Graham的RAG引擎",
40    )
41
42    to_html_tool = FunctionTool.from_defaults(fn=toHtml)
43
44    agent = ReActAgent.from_tools(
45        [to_html_tool,yc_tool],
46        verbose=True,
47    )
48
49    # 执行查询
50    logger.info("agent 查询结果:")
51    response = agent.chat(query)
52
53    print(response)
54
55    logger.info("\n查询完成")
56
57def main():
58    run_glm4_query_with_embeddings("请描述一下作者的求学经历,并将英文用html高亮显示")
59
60if __name__ == "__main__":
61    main()

可以看到,yc_tool 是我们通过加载本地文件自定义的一个RAG 应用,然后我们把它加入到 agent的 from tools中。

以下是加入 RAG 以后的 agent 的输出

 1INFO:llamaindex_demo.config:agent 查询结果:
 2> Running step 28bfc6bb-5e8a-4d5b-8273-36791b222a90. Step input: 请描述一下作者的求学经历,并将英文用html高亮显示
 3INFO:httpx:HTTP Request: POST https://open.bigmodel.cn/api/paas/v4/chat/completions "HTTP/1.1 200 OK"
 4Thought: The current language of the user is: Chinese. I need to use a tool to help me answer the question.
 5Action: query_engine_tool
 6Action Input: {'input': '请描述一下作者的求学经历'}
 7INFO:httpx:HTTP Request: POST https://open.bigmodel.cn/api/paas/v4/embeddings "HTTP/1.1 200 OK"
 8INFO:httpx:HTTP Request: POST https://open.bigmodel.cn/api/paas/v4/chat/completions "HTTP/1.1 200 OK"
 9Observation: 作者的求学经历可以概括如下:
10
111. **研究生阶段**:
12   - 作者最初在研究生院学习,但似乎对所选主题(连续性应用程序)并不满意,认为应该选择更有探索空间的宏和嵌入式语言。
13   - 作者的主要目标是尽快完成学业,摆脱研究生院。
14
152. **申请艺术学校**:
16   - 在研究生期间,作者同时申请了美国的罗德岛设计学院(RISD)和意大利佛罗伦萨的Accademia di Belli Arti。
17   - RISD录取了作者,而Accademia di Belli Arti的回复因邮寄错误延迟。
18
193. **在RISD的学习**:
20   - 作者被RISD视为转学二年级生,需要在夏季完成基础课程,包括绘画、色彩和设计等。
21   - 尽管如此,作者在基础课程中表现不错。
22
23......
24
25总体来说,作者的求学经历充满了探索和转变,从研究生院的学术研究到艺术学校的实践学习,再到通过个人项目和工作的经济独立,展现了其对知识和职业发展的不断追求和反思。
26> Running step c8f8eee2-ab9b-45c9-a05e-6136f688cae3. Step input: None
27INFO:httpx:HTTP Request: POST https://open.bigmodel.cn/api/paas/v4/chat/completions "HTTP/1.1 200 OK"
28Thought: The current language of the user is: Chinese. I need to use a tool to help me highlight the English words in the provided text.
29Action: toHtml
30Action Input: {'text': '作者的求学经历可以概括如下:\n\n1. **研究生阶段**:\n   - 作者最初在研究生院学习,但似乎对所选主题(连续性应用程序)并不满意,认为应该选择更有探索空间的宏和嵌入式语言。\n   - 作者的主要目标是尽快完成学业,摆脱研究生院。\n\n2. **申请艺术学校**:\n   - 在研究生期间,作者同时申请了美国的罗德岛设计学院(RISD)和意大利佛罗伦萨的Accademia di Belli Arti。\n   - RISD录取了作者,而Accademia di Belli Arti的回复因邮寄错误延迟。\n\n3. **在RISD的学习**:\n   - 作者被RISD视为转学二年级生,需要在夏季完成基础课程,包括绘画、色彩和设计等。\n   - 尽管如此,作者在基础课程中表现不错。\n\n4. **意外的Accademia邀请**:\n   - 夏末时,作者意外收到Accademia的入学考试邀请,决定前往佛罗伦萨。\n   - 作者通过节俭生活和之前的咨询工作积蓄,勉强支付了生活和学习的费用。\n\n5. **在Accademia的学习**:\n   - 作者发现Accademia的绘画系存在一种默契,即学生和教职员工互不干涉,维持着19世纪工作室的传统。\n   - 这种教育模式让作者感到失望。\n\n6. **秘密项目与经济独立**:\n   - 在Accademia期间,作者秘密从事《论Lisp》的工作,并获得了出版合同和一笔可观的收入。\n   - 这笔收入帮助作者还清了大学贷款,并积攒了回RISD的费用。\n\n7. **在Interleaf的工作经历**:\n   - 作者在Interleaf公司学到了许多关于科技公司的管理和技术开发的见解。\n   - 最重要的是,作者领悟到“低端吞噬高端”的市场策略。\n\n8. **回到RISD**:\n   - 作者在秋季回到RISD继续学习,发现真正的艺术学校与Accademia没有太大不同。\n   - 作者观察到绘画系的教育相对松散,而其他系如纺织、插画和建筑则更为严格。\n\n总体来说,作者的求学经历充满了探索和转变,从研究生院的学术研究到艺术学校的实践学习,再到通过个人项目和工作的经济独立,展现了其对知识和职业发展的不断追求和反思。'}
31Observation: 作者的求学经历可以概括如下:
32
331. **研究生阶段**:
34   - 作者最初在研究生院学习,但似乎对所选主题(连续性应用程序)并不满意,认为应该选择更有探索空间的宏和嵌入式语言。
35   - 作者的主要目标是尽快完成学业,摆脱研究生院。
36
372. **申请艺术学校**:
38   - 在研究生期间,作者同时申请了美国的罗德岛设计学院(<span style="color:red;">RISD</span>)和意大利佛罗伦萨的Accademia <span style="color:red;">di</span> <span style="color:red;">Belli</span> <span style="color:red;">Arti</span>。
39   - RISD录取了作者,而Accademia <span style="color:red;">di</span> <span style="color:red;">Belli</span> Arti的回复因邮寄错误延迟。
40
413. **在RISD的学习**:
42   - 作者被RISD视为转学二年级生,需要在夏季完成基础课程,包括绘画、色彩和设计等。
43   - 尽管如此,作者在基础课程中表现不错。
44
45......
46
47总体来说,作者的求学经历充满了探索和转变,从研究生院的学术研究到艺术学校的实践学习,再到通过个人项目和工作的经济独立,展现了其对知识和职业发展的不断追求和反思。
48> Running step 649456af-f363-47c9-bd06-3c36d1ff48c6. Step input: None
49INFO:httpx:HTTP Request: POST https://open.bigmodel.cn/api/paas/v4/chat/completions "HTTP/1.1 200 OK"
50Thought: I can answer without using any more tools. I'll use the user's language to answer.
51Answer: 作者的求学经历可以概括如下:
52
531. **研究生阶段**:
54   - 作者最初在研究生院学习,但似乎对所选主题(连续性应用程序)并不满意,认为应该选择更有探索空间的宏和嵌入式语言。
55   - 作者的主要目标是尽快完成学业,摆脱研究生院。
56
572. **申请艺术学校**:
58   - 在研究生期间,作者同时申请了美国的罗德岛设计学院(<span style="color:red;">RISD</span>)和意大利佛罗伦萨的Accademia <span style="color:red;">di</span> <span style="color:red;">Belli</span> <span style="color:red;">Arti</span>。
59   - RISD录取了作者,而Accademia <span style="color:red;">di</span> <span style="color:red;">Belli</span> Arti的回复因邮寄错误延迟。
60
613. **在RISD的学习**:
62   - 作者被RISD视为转学二年级生,需要在夏季完成基础课程,包括绘画、色彩和设计等。
63   - 尽管如此,作者在基础课程中表现不错。
64
65......
66
67总体来说,作者的求学经历充满了探索和转变,从研究生院的学术研究到艺术学校的实践学习,再到通过个人项目和工作的经济独立,展现了其对知识和职业发展的不断追求和反思。
68作者的求学经历可以概括如下:
69
701. **研究生阶段**:
71   - 作者最初在研究生院学习,但似乎对所选主题(连续性应用程序)并不满意,认为应该选择更有探索空间的宏和嵌入式语言。
72   - 作者的主要目标是尽快完成学业,摆脱研究生院。
73
742. **申请艺术学校**:
75   - 在研究生期间,作者同时申请了美国的罗德岛设计学院(<span style="color:red;">RISD</span>)和意大利佛罗伦萨的Accademia <span style="color:red;">di</span> <span style="color:red;">Belli</span> <span style="color:red;">Arti</span>。
76   - RISD录取了作者,而Accademia <span style="color:red;">di</span> <span style="color:red;">Belli</span> Arti的回复因邮寄错误延迟。
77
783. **在RISD的学习**:
79   - 作者被RISD视为转学二年级生,需要在夏季完成基础课程,包括绘画、色彩和设计等。
80   - 尽管如此,作者在基础课程中表现不错。
81
82......
83
84总体来说,作者的求学经历充满了探索和转变,从研究生院的学术研究到艺术学校的实践学习,再到通过个人项目和工作的经济独立,展现了其对知识和职业发展的不断追求和反思。
85INFO:llamaindex_demo.config:
86查询完成

可以看出,在它的思考过程中,应用了我们的 toHtml 方法,将英文单词就用html标签包裹。

agent 记忆

agent 还有记忆的功能,也就是说之前问过的问题,它会记得,比如我们在一个流程中发起了多次提问

Image

agent 不会把每次的提问当做一个独立提问,它是知道上下文的,这就是它的记忆功能。

附录

附上之前文章的链接方便查阅:

如何用 30秒和 5 行代码写个 RAG 应用?

提速 RAG 应用:用 DeepSeek API 替换本地 Ollama 模型,LlamaIndex 实战解析

提升RAG应用性能:使用智谱AI的GLM-4和Embedding-3模型优化文档检索

Milvus实战:如何用一个数据库提升你的AI项目性能

位旅人路过 次翻阅 初次见面