在生成式 AI 领域,智能体(Agent)已成为创新的关键。它们使大型语言模型(LLM)具备更强的推理能力,并能执行如调用外部数据源等复杂任务,例如进行 Google 搜索、调用 API 或生成个性化图像。
本文将教你如何构建类似的解决方案,让你能完全掌控所选的 LLM,处理私有数据、调用外部 API 等。
我开发了一个多模态聊天机器人,整合了以下技术栈:
•LangChain•ChatGPT•DALL·E 3•Streamlit(用于构建用户界面)
最后我还会分享该项目的开源代码仓库,供你自由探索和部署。
LangChain 框架中 Agents 的工作机制
Agent 的执行流程如下:
1.接收用户的任务或请求 2.通过 LLM 进行推理,将任务拆解为多个步骤 3.调用合适的工具执行任务 4.将工具的结果再次交由 LLM 分析 5.持续循环,直到问题解决,返回最终结果
该机制使得 Agent 可以动态适配任务,灵活调用工具,并持续调整流程直至输出满意答案。
三大核心工具如何提供可行的解决方案
我构建的多模态聊天机器人由一个 Agent 驱动,该 Agent 集成了以下三种工具:
1.REST Countries API Chain
用于调用公开的国家信息 API[1],以获取国家相关的基础数据。2.DALL·E 3 图像生成器
根据国家名称生成相应的图像(如国旗、地貌、文化元素等)。3.Google 搜索工具
用于从网页中检索更多实时信息,填补模型知识的空白。
我使用 Python 编写了整个聊天机器人。以下是创建该 Agent 的部分代码片段:
我使用 Python 开发了整个多模态聊天机器人,以下是创建智能体(Agent)的核心函数代码:
def create_agent():
# 定义可用的工具:图像生成、国家信息查询、Google 搜索
tools =[countries_image_generator, get_countries_by_name, google_search]
# 将这些工具包装为 OpenAI 支持的 Function Calling 格式
functions =[convert_to_openai_function(f)for f in tools]
# 初始化 LLM 模型,并绑定功能(function calling)
model =ChatOpenAI(model_name="gpt-3.5-turbo-0125").bind(functions=functions)
# 构建提示词模板,包含系统设定、历史记录、用户输入和 agent 的 scratchpad
prompt =ChatPromptTemplate.from_messages([
("system","你是一个有帮助但带点毒舌的助手"),
MessagesPlaceholder(variable_name="chat_history"),
("user","{input}"),
MessagesPlaceholder(variable_name="agent_scratchpad")
])
# 定义对话记忆体,保留最近 5 条消息
memory =ConversationBufferWindowMemory(
return_messages=True,
memory_key="chat_history",
k=5
)
# 构建 Agent 执行链(Chain)
chain =(
RunnablePassthrough.assign(
agent_scratchpad=lambda x: format_to_openai_functions(x["intermediate_steps"])
)
| prompt
| model
|OpenAIFunctionsAgentOutputParser()
)
# 构建 Agent 执行器,绑定工具、链条和记忆
agent_executor =AgentExecutor(
agent=chain,
tools=tools,
memory=memory,
verbose=True# 启用详细输出,便于调试
)
return agent_executor
LangChain 框架为智能体提供了一个全面的解决方案,能够无缝整合各种组件,例如提示模板、记忆管理、大语言模型、输出解析器,以及将这些元素编排到一个智能体执行器中。
create\_agent()
函数是这一方法的核心,旨在实例化并配置一个带有特定功能的 ChatGPT 智能体,它集成了外部工具和一个用于处理用户输入与生成响应的自定义处理流程。
以下是该函数各组成部分的解析:
•Tools Integration(工具集成) : 定义了一组工具,包括 countries\_image\_generator
、get\_countries\_by\_name
和 google\_search
。这些工具随后被转换为 OpenAI 函数,从而能够在 ChatGPT 的处理流程中被调用。•Model Configuration(模型配置) : 该函数设置了一个 ChatGPT 模型,具体为 “gpt-3.5-turbo-0125”。该模型通过绑定前述的 OpenAI 函数获得增强,使其在运行中能够调用这些外部工具。•Prompt Template(提示模板) : 使用一系列预设消息创建 ChatPromptTemplate
,包括系统定义的角色信息,以及用于动态内容的占位符(如用户输入和中间推理内容)。此模板用于指导对话流程和结构。•Memory Management(记忆管理) : 使用 ConversationBufferWindowMemory
来管理对话历史,存储最近 5 条消息(通过参数 k
控制)。该记忆以 “chat_history” 为索引,并配置为返回消息以用于生成回复。•Processing Pipeline(处理流程) : 定义了一个处理链,从 RunnablePassthrough
(处理中间步骤)开始,再将上下文依次传递给准备好的提示模板、ChatGPT 模型,以及 OpenAIFunctionsAgentOutputParser
。此流程编排了数据在智能体中的流动,将模型输出与函数调用结果集成并解析。该流程使用的是 LangChain 表达式[2]语言(LCEL),这是一种声明式方式,可方便地组合各处理链组件。•Agent Executor(智能体执行器) : 该函数的核心是创建一个 AgentExecutor
,它封装了整个 Agent,包括工具、记忆管理和定义的处理流程。这个执行器运行 Agent,处理输入,并根据配置生成输出。
在 LangChain 中创建自定义工具
使用 @tool
装饰器是 LangChain 框架中定义自定义工具最简单的方式。该装饰器默认使用函数名作为工具名,但你也可以通过传递字符串作为第一个参数来覆盖默认名称。
此外,装饰器还会使用函数的文档字符串(docstring)作为工具的描述,因此必须提供 docstring 。
你还可以通过将工具名称和 JSON 参数格式传递给装饰器,进一步自定义工具(参见下方第二个工具 get\_countries\_by\_name
的用法)。
智能体会将工具的描述信息作为上下文提供给大语言模型(LLM),以便 LLM 判断应调用哪个工具。因此,选择合适的描述信息至关重要 。
下面是我对每个工具的使用体会:
1)国家图像生成工具
@tool
def countries_image_generator(country: str):
"""调用此工具可获取某个国家的图像"""
res =DallEAPIWrapper(model="dall-e-3").run(
f"You generate image of a country representing the most typical country's characteristics, incorporating its flag. the country is {country}"
)
answer_to_agent =(f"Use this format- Here is an image of {country}: [{country} Image]"
f"url= {res}")
return answer_to_agent
我使用了 DallEAPIWrapper
来调用 DALL·E 3 模型,并为其提供了明确的图像生成指令,例如:要求生成的国家图像应展现该国最典型的特征,并融入其国旗元素等。
此外,我还在返回给 Agent 的响应中添加了输出格式说明 ,明确指定输出格式需包含国家名称和生成图像的 URL。这一点非常关键,因为它能帮助智能体识别该响应是一个“图像”内容,而不仅仅是普通文本。
2)国家信息查询工具
def prepare_and_log_request(base_url: str,params:Optional[dict]=None)->PreparedRequest:
"""准备请求并打印完整 URL(用于日志记录)"""
req =PreparedRequest()
req.prepare_url(base_url,params)
print(f'\033[92mCalling API: {req.url}\033[0m')
return req
# 查询参数定义:限制可选字段,最多 27 个字段
classParams(BaseModel):
fields:Optional[conlist(str, min_items=1, max_items=27)]=Field(
default=None,
description='用于过滤请求返回结果的字段列表。',
examples=["name","topLevelDomain","alpha2Code","alpha3Code","currencies","capital","callingCodes","altSpellings","region","subregion","population","latlng","demonym","area","gini","timezones","borders","nativeName","numericCode","languages","flag","regionalBlocs","cioc"]
)
# 路径参数:国家名称
classPathParams(BaseModel):
name: str =Field(..., description='国家名称')
# 请求数据模型:包含路径参数和查询参数
classRequestModel(BaseModel):
params:Optional[Params]=None
path_params:PathParams
# 定义工具,并使用 args_schema 指定请求模型结构
@tool(args_schema=RequestModel)
def get_countries_by_name(path_params:PathParams,params:Optional[Params]=None):
"""当你需要回答与国家相关的问题时非常有用。输入应为一个完整的问题。"""
BASE_URL = f'https://restcountries.com/v3.1/name/{path_params.name}'
# 如果有字段限制参数,拼接为查询字符串;否则设为 None
effective_params ={"fields":",".join(params.fields)}ifparamsandparams.fields elseNone
# 构建并记录请求
req = prepare_and_log_request(BASE_URL, effective_params)
# 发送 GET 请求
response = requests.get(req.url)
# 如果请求失败则抛出异常
response.raise_for_status()
return response.json()
在使用这个工具的过程中,我遇到了一个特别的挑战。第一步是设计一个用于调用 REST API(按国家名称获取国家信息)的参数模型。为此,我构建了一个包含路径参数和查询参数的 Pydantic 模型。
随后,我将这个模型集成到了 LangChain 的 @tool
装饰器中。
这种做法的价值在于:我们不仅仅是简单提供工具概述,而是将完整的参数模型结构 传递给大语言模型(LLM),包括每个参数的详细描述信息。
这一方法显著提升了 LLM 判断输入参数的准确性,使其能够根据用户的自然语言提示,更精确地确定函数应使用哪些参数,从而实现更准确、高效的人机交互。
3)Google 搜索工具
@tool
def google_search(query: str):
"""使用提供的查询字符串执行 Google 搜索。需要获取当前数据时请选择此工具"""
returnSerpAPIWrapper().run(query)
为了通过 API 在 Google 上执行搜索,我使用了内置的 SerpAPIWrapper
。需要特别说明的是:该工具运行需要提供一个 API 密钥 。
你可以在我的 GitHub 仓库的 README[3] 中了解如何获取这个密钥。
🤖 多模态聊天机器人的系统架构
下图展示了该多模态聊天机器人系统的结构与运行流程:
1.提示词优化 :用户最初的输入,以及对话历史的上下文,会一并发送给 LLM(本案例中使用的是 ChatGPT),以生成一个更精准、结构更清晰的查询指令。2.思考过程 :Agent 将优化后的提示词(以及可选的工具列表)传递给 LLM,进行推理和决策。
LLM 根据上下文和目标判断应调用哪个工具,或直接生成最终回答。如果此阶段已得出答案,Agent 会直接将其返回给用户。3.工具调用 :Agent 执行 LLM 所选定的工具。例如调用国家信息 API、图像生成器或 Google 搜索工具。4.观察与反馈 :工具返回的输出结果,会再次被送回 LLM,供其继续推理,整合上下文并生成最终答复。
示例实现:运行多模态聊天机器人
示例一:生成荷兰图像
•提问:“请生成一张荷兰的图像”•Agent 调用 countries\_image\_generator("Holland")
,并输入了所需参数:国家名称。•LLM 生成提示,DALL·E 3 返回图像链接;•Agent 解析返回,输出格式统一
示例二:获取旅游数据(使用 Google 搜索)
•提问:“去年有多少游客访问了雅典?”•Agent 调用 google\_search(通过搜索关键词"number of tourists visited Athens last year")
•返回结果:约 640 万游客。
示例三:查询国家信息
•提问:“巴西有哪些地区和子地区?”•接着提问:“那里的货币和首都是什么?”•两个问题都使用了 get\_countries\_by\_name
工具。
例如,对于第二个问题,调用的参数是:
{
"path_params":{
"name":"Brazil"
},
"params":{
"fields":["currencies","capital"]
}
}
此外,这个多模态聊天机器人还能够记住之前的对话内容, 如在关于“货币和首都”的问题中,它能够正确地推断出所指的国家是上一个问题中的“巴西”。
最后,我让它生成了一张希腊的图像:
🧰 框架与技术选型建议
构建多模态智能体的关键在于:选对工具 + 选对模型
✅ 推荐技术选型:
| 类别 | 工具/框架 | 说明 | | 智能体框架 | LangChain | 提供完整的 Agent、工具集成、记忆体、函数调用链 | | LLM 模型 | GPT-3.5-turbo-0125 | 支持 Function Calling,响应速度快 | | 搜索 API | SerpAPI | 封装 Google 搜索结果 | | 图像生成 | DALL·E 3(via OpenAI API) | 多模态图像创作能力 | | UI 交互 | Streamlit | 简洁易部署的 Python Web 框架 |
📎 项目总结与建议
•✅ LangChain 提供了构建模块化 Agent 的强大能力;•✅ 多模态机器人可扩展性强,适用于旅游、教育、问答、内容生成等场景;•✅ 使用支持函数调用的 LLM 可极大提升智能体交互的精准度;•✅ 工具的定义需明确输入输出,配合文档字符串提供语义描述,有利于 LLM 选择最优工具;•✅ 强烈建议提前申请好相关 API key(如 SerpAPI、OpenAI);
📚 延伸阅读
你可以在我的 GitHub 仓库中查看更多项目源码和使用说明:
👉 🔗 项目仓库地址(GitHub)[4])
✨ 最终建议
“选择合适的工具 + 使用支持 Function Calling 的 LLM,是构建强大 AI 应用的关键。”
量身定制的智能体不仅能听懂、理解、响应,还能行动、执行、生成。
这正是下一代 AI 产品的雏形,也是你可以立即动手实现的现实方案。
References
[1]
公开的国家信息 API: https://restcountries.com/
[2]
LangChain 表达式: https://python.langchain.com/docs/concepts/lcel/
[3]
GitHub 仓库的 README: https://github.com/nirbar1985/country-compass-ai/blob/main/README.md
[4]
🔗 项目仓库地址(GitHub): https://github.com/nirbar1985/country-compass-ai