当前位置: 首页 > wzjs >正文

中国建设银行网站上不去无锡百度推广公司哪家好

中国建设银行网站上不去,无锡百度推广公司哪家好,企业如何利用互联网开展营销,做视频网站用哪个模板文章目录 准备用于查天气的工具建立矢量数据库创建检索器(retriever)准备工具集创建智能体测试智能体总结代码 在上一篇文章中,我们实践了如何使用 langchain 创建一个简单的 ReAct Agent(智能体),这次我们升级一下玩法:实现一个可以调用两个…

文章目录

    • 准备
    • 用于查天气的工具
    • 建立矢量数据库
    • 创建检索器(retriever)
    • 准备工具集
    • 创建智能体
    • 测试智能体
    • 总结
    • 代码


在上一篇文章中,我们实践了如何使用 langchain 创建一个简单的 ReAct Agent(智能体),这次我们升级一下玩法:实现一个可以调用两个 工具 的智能体。
其中一个工具用户查询天气预报,另外一个工具用来查询知识库,实际上该工具实现了 RAG(Retrieval Augmented Generation,检索增强生成)

此次我们使用 qwen2.5, llama3.1deepseek 。感觉在处理中文方面,感觉 qwen2.5llama3.1 要好一些。

准备

在正式开始撸代码之前,需要准备一下编程环境。

  1. 计算机
    本文涉及的所有代码可以在没有显存的环境中执行。 我使用的机器配置为:

    • CPU: Intel i5-8400 2.80GHz
    • 内存: 16GB
  2. Visual Studio Code 和 venv
    这是很受欢迎的开发工具,相关文章的代码可以在 Visual Studio Code 中开发和调试。 我们用 pythonvenv 创建虚拟环境, 详见:
    在Visual Studio Code中配置venv。

  3. Ollama
    Ollama 平台上部署本地大模型非常方便,基于此平台,我们可以让 langchain 使用 llama3.1qwen2.5deepseek 等各种本地大模型。详见:
    在langchian中使用本地部署的llama3.1大模型 。

用于查天气的工具

我们构建一个简单的工具,它用于查询某个城市的天气情况:

@tool(parse_docstring=True)
def get_wheather_info(city_name: str = ''  #不设置默认值可能导致LLM强行解析city_name出错或者强行调用这个tool
) -> str:"""获取某个城市的天气信息。Args:city_name: 城市名称。        """print(f'Getting weather information for:{city_name}')if not city_name:return "缺少 city_name 参数,无法检索天气信息。"        """**这个返回很重要**返回错误后,agent会放弃这个结果,用自己的能力回答问题,这样结果更加合理;否则,agent会使用空的city_name调用这个tool,并且会拼凑出new york的天气或者别的天气方面的信息。"""else:return f"{city_name}的气温是25摄氏度。"

通过设定参数默认值、在代码中对参数做有效性校验,并在参数有问题返回明确的信息,可以有效的防止 LLM(大语言模型) 乱用工具函数。

我们通过下面的方法测试一下:

def test_get_wheather_info(llm_model_name,city_name):"""测试获取天气信息"""print(f'--------{llm_model_name}----------')tools = [get_wheather_info,]llm = ChatOllama(model=llm_model_name,temperature=0,verbose=True)llm_with_tools = llm.bind_tools(tools)query = f'{city_name}的天气怎么样?'ai_msg = llm_with_tools.invoke(query)print(f'get_wheather_info tool_calls:\n{ai_msg.tool_calls}')

由于 langchain 的 bind_tools 函数对 deepseek-r1 支持不好,执行时出错,所以我们使用 MFDoom/deepseek-r1-tool-calling:7b

qwen2.5llama3.1 生成的 tool_calls 一样:

[{'name': 'get_wheather_info', 'args': {'city_name': '北京'}, 'id': 'bf852b33-2e36-4cf9-a9ff-5eb8bb3b7ae3', 'type': 'tool_call'}
]

MFDoom/deepseek-r1-tool-calling:7b 生成的 tool_calls 比较复杂一些:

[{'name': 'function name', 'args': {'param1': 'value1'}, 'id': 'e7698bda-c759-4d8f-903b-239447c9b2c1', 'type': 'tool_call'}, {'name': 'get_wheather_info', 'args': {'city_name': 'Beijing'}, 'id': '240a07a7-1a65-4bbc-9851-92d86c9edbe8', 'type': 'tool_call'}, {'name': 'get_wheather_info', 'args': {'city_name': 'Beijing'}, 'id': '735b4aee-b58e-4d38-8c82-980656f65c77', 'type': 'tool_call'}
]

不知为什么要把 北京 转换成 Beijing

建立矢量数据库

我们使用网页做内容源,拆分并矢量化后,存储在本地。
下面先定义处理矢量数据库的类:

class LocalVectorDBChroma:"""使用Chroma在本地处理适量数据库"""def __init__(self,model_name,persist_directory,delimiter = ","):self._embedding = OllamaEmbeddings(model=model_name)self._persist_directory = persist_directoryself._delimiter = delimiterdef get_vector_store(self):return Chroma(persist_directory=self._persist_directory,embedding_function=self._embedding)def embed_documents_in_batches(self,documents,batch_size=3):"""按批次嵌入,可以显示进度。vectordb会自动持久化存储在磁盘。"""vectordb = Chroma(persist_directory=self._persist_directory,embedding_function=self._embedding)for i in tqdm(range(0, len(documents), batch_size), desc="嵌入进度"):batch = documents[i:i + batch_size]# 从文本块生成嵌入,并将嵌入存储在本地磁盘。vectordb.add_documents(batch)def embed_webpage(self,url):"""嵌入网页"""from langchain_community.document_loaders import WebBaseLoaderloader = WebBaseLoader(url,encoding="utf-8")    # 增加encoding参数防止中文乱码docs = loader.load()documents = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200).split_documents(docs)self.embed_documents_in_batches(documents)

将文本转化成矢量的过程称之为嵌入,语义相近的文本转化的矢量之间的空间距离较短,所以在做矢量检索时,可以根据语义而不是关键词来查找相近的结果。
网页的段落文字通常较多,所以这里设置的 chunk_size 也比较大。

执行嵌入:

from common.MyVectorDB import LocalVectorDBChroma
def create_db(model_name):    """生成本地矢量数据库"""persist_directory = get_persist_directory(model_name)if os.path.exists(persist_directory):returndb = LocalVectorDBChroma(model_name,persist_directory)    db.embed_webpage("http://wfcoding.com/articles/programmer/p0102/")

执行完毕上述代码后,本地硬盘上应该会多一个文件夹 es_shaw ,该文件夹中的内容就是矢量数据。

创建检索器(retriever)

下面我们可以基于已创建的矢量数据库,创建 检索器(retriever)

def create_retriever(embed_model_name):"""创建检索器"""persist_directory = get_persist_directory(embed_model_name)db = LocalVectorDBChroma(embed_model_name,persist_directory)# 基于Chroma 的 vector store 生成 检索器vector_store = db.get_vector_store()retriever = vector_store.as_retriever(search_type="similarity",search_kwargs={"k": 2},)return retriever

此检索器返回2篇文档,由于网页内容已经被切割,这样做可以避免只有一篇文档容易导致检索出来的内容不完整的问题。

准备工具集

下面我们用准备好的检索器生成一个工具:elastic_search,再组合另外一个查询天气的工具,创建工具集:

def create_tools(embed_model_name):"""创建工具集"""retriever_tool = create_retriever_tool(create_retriever(embed_model_name),"elastic_search","当您搜索有关 elasticsearch 的知识时才使用此工具!",)tools = [get_wheather_info, retriever_tool]return tools

我们步步为营,先定义一个函数,来测试一下工具集是否能够正常生成 tool_calls,只有可以正常生成 tool_calls,智能体才可能正确的调用工具函数。

def test_tools(llm_model_name,embed_model_name,querys):"""测试工具集"""llm = ChatOllama(model=llm_model_name,temperature=0,verbose=True)tools = create_tools(embed_model_name)llm_with_tools = llm.bind_tools(tools)print(f'--------{llm_model_name}----------')for query in querys:response = llm_with_tools.invoke([HumanMessage(content=query)])print(f"ContentString:\n {response.content}")print(f"ToolCalls: \n{response.tool_calls}")

我们用三个问题进行测试,看看三个大模型表现如何:

querys = ["你好,你擅长能做什么?","上海的天气怎么样?","如何实现elasticsearch的深度分页?"]
  • qwen2.5
ContentString:
您好!我可以帮助您查询天气信息、回答一些常识性问题或者提供关于Elasticsearch的知识。请告诉我具体需要什么帮助呢?
ToolCalls:
[]
ContentString:ToolCalls:
[{'name': 'get_wheather_info', 'args': {'city_name': '上海'}, 'id': '3151df40-6b67-4683-8f68-2bd453d4cbed', 'type': 'tool_call'}]
ContentString:ToolCalls:
[{'name': 'elastic_search', 'args': {'query': '如何实现elasticsearch的深度分页'}, 'id': '7612018c-c63b-4e03-8321-478240944a99', 'type': 'tool_call'}]

qwen2.5 做得很棒,非常聪明的处理了所有问题。甚至在回答第一个问题时,把工具的功能也融合进去了。

  • llama3.1
ContentString:ToolCalls:
[{'name': 'get_wheather_info', 'args': {'city_name': ''}, 'id': 'c13eb0b9-8aa8-48fc-9d32-3511795a57d1', 'type': 'tool_call'}]
ContentString:ToolCalls:
[{'name': 'get_wheather_info', 'args': {'city_name': '北京'}, 'id': 'c6ad345f-ecc3-494a-80a8-47a65b657bea', 'type': 'tool_call'}]
ContentString:ToolCalls:
[{'name': 'elastic_search', 'args': {'query': 'elasticsearch 深度分页'}, 'id': 'd154d4f6-d129-4298-a36f-25d5b957538d', 'type': 'tool_call'}]

llama3.1 在处理第1个问题时,生成了一个无效的 tool_calls ; 在处理第2个问题时,把 city_name 错误的推理成了 北京 ,只有第1个问题推理得准确无误。

  • MFDoom/deepseek-r1-tool-calling:7b
ContentString:ToolCalls:
[{'name': 'get_wheather_info', 'args': {'city_name': '上海'}, 'id': '4db2f88b-b76a-4d39-8673-8fea228f375c', 'type': 'tool_call'}]
ContentString:ToolCalls:
[{'name': 'get_wheather_info', 'args': {'city_name': '上海'}, 'id': 'c5c7b229-f99d-4057-8c21-af679ffba827', 'type': 'tool_call'}]
ContentString:ToolCalls:
[{'name': 'get_wheather_info', 'args': {'city_name': '上海'}, 'id': '303273d4-5edb-47ed-b2ab-8f26200df6cd', 'type': 'tool_call'}]

MFDoom/deepseek-r1-tool-calling:7b 也只有第2个问题推理正确。

创建智能体

工具集创建完毕了,现在该给它们装上大脑,即智能体了。
我们先用 create_tool_calling_agent 方法创建 智能体对象:

def create_agent(llm_model_name,embed_model_name):"""创建智能体"""from langchain_core.tools import render_text_descriptiontools = create_tools(embed_model_name)# 此prompt是基于hwchase17/openai-functions-agent修改的systemprompt = """\您是一名助理,有权使用以下工具集。下面是每个工具的名称和说明:[get_wheather_info, elastic_search]- **仅在需要时使用上述工具集!**- 如果没有可靠的依据来确定 city_name,则不要调用 get_wheather_info!""" prompt = ChatPromptTemplate([("system", systemprompt),("placeholder", "{chat_history}"),("human", "{input}"),("placeholder", "{agent_scratchpad}"),])llm = ChatOllama(model=llm_model_name,temperature=0,verbose=True)agent = create_tool_calling_agent(llm, tools, prompt)return agent

再创建 一个 AgentExecutor ,创建完毕后,就可以执行这个智能体了:

def create_agent_executor(llm_model_name,embed_model_name):"""创建agent_executor"""tools = create_tools(embed_model_name)agent = create_agent(llm_model_name,embed_model_name)    agent_executor = AgentExecutor(agent=agent, tools=tools)"""实际上在create_agent中已经创建了tools,这里还要再传入tools,似乎有点多余。"""return agent_executor

测试智能体

下面我们定义测试方法,用不同的大模型执行这个智能体:

def test_agent_executor(llm_model_name,embed_model_name,querys):"""测试AgentExecutor"""print(f'--------{llm_model_name}----------')agent_executor = create_agent_executor(llm_model_name,embed_model_name)for query in querys:r = agent_executor.invoke({"input": query})print(f'agent_executor.invoke:\n{r}')

下面就是见证奇迹的时刻了,我们还是用同样的3个问题,用不同的大模型驱动智能体。

querys = ["你好,你擅长能做什么?","上海的天气怎么样?","如何实现elasticsearch的深度分页?"]
  • qwen2.5
{'input': '你好,你擅长能做什么?', 'output': '我可以帮助您查询天气信息、或者在特定情况下搜索相关信息。请告诉我您需要什么具体帮助呢?例如,您可以询问某个城市的天气情况,或者有关 Elasticsearch 的问题。'} 
{'input': '上海的天气怎么样?', 'output': '上海现在的温度是25摄氏度。'}
{'input': '如何实现elasticsearch的深度分页?', 'output': '在 Elasticsearch 中,使用 `search_after` 实现深度分页是一种有效的方法。...
使用 `search_after` 可以有效地处理大量数据的分页问题。这种方法避免了因结果窗口过大而引发的问题,并且可以灵活地进行深度分页操作。\n\n希望这些信息对你有所帮助!如果你有任何其他问题,请随时提问。'}

qwen2.5 表现稳定,第2、3个问题显然是基于调用工具获取的信息,回答得很好!

  • llama3.1
{'input': '你好,你擅长能做什么?', 'output': '我可以使用工具集来帮助回答问题!例如,如果你问“今天是几月几号”,我可以使用 [get_wheather_info, elastic_search] 来获取当前日期的信息。如果你提供城市名称,我也可以 使用 get_wheather_info 来获取天气预报。'}
{'input': '上海的天气怎么样?', 'output': 'Sorry, 我没有找到关于上海的天气信息。您可以尝试提供更多信息或使用不同的工具来获取答案。'}
{'input': '如何实现elasticsearch的深度分页?', 'output': '答案:使用 search_after 进行深度分页可以避免 elasticsearch 中的 Result window is too large 错误,并且可以提高查询效率和减少内存占用。首先,确定一个可以唯一确定一条文档的键,然后在第一次查询中指定排序规则。在后续查询中,将第一次查询结果的最后一条记录的 sort 值作为 search_after 的参数,可以实现深度分页。'}

果然,llama3.1 在回答第2、3个问题时,并没有正确的调用工具。

  • MFDoom/deepseek-r1-tool-calling:7b
{'input': '你好,你擅长能做什么?', 'output': '<think>\n嗯,用户问:“你好,你擅长能做什么?” 这是一个常见的问候,我需要回应得友好且明确。首先,我要确认自己能够帮助用户解决什么类型的问题。\n\n根据提供的工具集,有两个可用的工具:get_wheather_info 和 elastic_search。get_wheather_info 用于获取某个城市的天气信息,但需要 city_name 参数,...。\n</think>\n\n您好!我目前 的功能主要是帮助您回答问题、提供信息和解答疑问等。如果您有具体的问题或需要帮助的地方,请随时告诉我,我会尽力为您提供详细的解答和帮助。\n\n如果需要调用某个工具(如 `get_wheather_info` 或 `elastic_search`),请 告诉我具体需求,并提供相关的参数,我会根据您的要求调用相应的工具来为您服务!'}
{'input': '上海的天气怎么样?', 'output': 'Agent stopped due to max iterations.'}
{'input': '如何实现elasticsearch的深度分页?', 'output': '</think>\n\n要实现Elasticsearch的深度分页,可以按照以下步骤操作:\n\n1. **定义搜索查询**:使用`get_wheather_info`获取所需城市的天气信息,并构建一个包含城市名称、温度等字段的查询。\n\n2. **设置分页参数**:\n  ... \n\n通过以上步骤,可以实现对Elasticsearch数据的深度分页功能。'}

可能因为提示提或者与 langchain 结合的不好,它也没有正确的调用工具。

总结

通过上述编码演练,我们发现使用 qwen2.5 作为 LLM(大语言模型) 处理中文比较稳定,驱动智能体也游刃有余;使用 shaw/dmeta-embedding-zh 做中文嵌入检索效果也不错,值得在 RAG(Retrieval Augmented Generation,检索增强生成) 场景中使用。
到现在,我们还不太清楚这个 Agent(智能体) 的思考过程,也并未填充提示词中的 {chat_history} 集成消息历史,我们在下一篇文章中再进一步完善。

代码

本文涉及的所有代码以及相关资源都已经共享,参见:

  • github
  • gitee

为便于找到代码,程序文件名称最前面的编号与本系列文章的文档编号相同。

🪐感谢您观看,祝好运🪐

http://www.dtcms.com/wzjs/28278.html

相关文章:

  • 网站开发分为几个方向百度广告服务商
  • 做游戏排行榜的网站模板网址查询域名
  • java电影网站开发视频如何写推广软文
  • linux做网站1G内存够不seo关键词优化是什么意思
  • 建设干部培训中心网站万网域名注册教程
  • 河北怎样做网站网络营销公司注册找哪家
  • 网站建设企业全国十大跨境电商排名
  • 自己有服务器怎么做网站搜关键词网站
  • 哪个网站专门做邮轮旅游的怎么在百度上发表文章
  • 做鲜花配送网站需要准备什么网络营销师证书需要多少钱
  • 广州网站建设信科便宜鹤壁搜索引擎优化
  • 毕业设计h5网站制作最新的疫情情况
  • 做科技公司的网站公司今天发生的重大新闻
  • 东莞公司网站建设小知识今天全国疫情最新消息
  • 北京网络公司有哪些seo数据统计分析工具有哪些
  • 网站建设色系搭配公司做个网站多少钱
  • 化妆品网站建设思路东莞网站自动化推广
  • 设计网站公司 生活湖南岚鸿搜索引擎分哪三类
  • 网站建设的目的是什么域名解析查询工具
  • b2b网站开发合同商务网站建设
  • 焦作建设网站的公司网站在线制作
  • 做网站定金是多少钱网站快速优化排名排名
  • 网站地图模板下载怎样在百度答题赚钱
  • 旅游电子商务网站的建设淘宝搜索关键词排名查询工具
  • dede单本小说网站源码关键词投放
  • 福州网站建设网络公司南宁seo公司哪家好
  • 免费的发帖收录网站在哪里做推广效果好
  • 企业还有人做网站么网站超级外链
  • app是如何开发出来的杭州seo按天计费
  • p2p网站建设公司排名各种推广平台