在我开始构建第一个商用AI代理三个月之后,在演示给客户时,一切都出问题了。
本应顺畅的自动工作流程变成了一个令人尴尬的反复澄清请求和决策不一致的循环。客户虽然保持礼貌,但显然有些失望。
他们离开后,我花了好几个小时分析失败的原因。我发现我对代理架构的理解根本错了——我建立了一个过于复杂的结构,决策界限模糊且没有清晰的推理路径。
那次失败改变了我的方式,并也为我解释这些系统提供了基础。一旦理解了核心原则的理解,构建有效代理就变得相当简单了。
我真是受不了别人把这事搞得这么复杂,所以我们还是尽量简单点吧.
AI代理系统是这样的系统:(1)逐步解决问题,(2)在需要时连接到外部工具,(3)从行动中学习,以随时间提高表现。
与只会根据提示回应的聊天机器人不一样,代理会主动并独立完成任务。两者之间的区别在于,一个是回答数据问题,另一个是分析数据。
从模型到智能代理在没有代理的情况下,我们构建了独立且分离的AI解决方案——一个用来理解文本的模型,一个用来生成代码的,还有一个用来处理图像的。
这种碎片化的做法(1)迫使用户手动处理工作流程,(2)在不同系统间切换时,上下文会丢失,(3)需要为每个步骤搭建定制化的集成。
代理们转换了这种模式。
与处理单一任务的传统模型不同,代理可以管理多种能力,并同时确保对整个任务有全面的理解。
代理不仅只是遵循指令——它们会根据学到的东西灵活应对,并做出明智的决定,就像我们人类一样。
AI的核心优势是什么我们可以通过研究Medium上的文章来了解一下这些代理人的能力。
传统的人工智能将这分解为孤立的步骤,比如总结、提取关键词、分类以及生成见解,每个步骤都需要人工干预。
限制不仅在于模型各自独立运作的问题,还在于必须手动安排整个流程,明确地在各步骤间传递知识,并根据中间结果决定需要进行哪些额外操作。
基于代理的方法与这不同的是,自主地进行每一步,同时也不失去对大目标的视野。
智能代理的构建要素AI代理程序可以概括为三个基本原则如下:
- 状态管理: 代理的工作记忆跟踪它所学到的信息及其目标任务的上下文
- 决策制定: 代理根据当前的知识决定采取哪种方法有意义
- 工具使用: 代理知道解决每个特定问题的工具有哪些
既然你已经理解了什么是AI代理以及为什么它们很重要,我们用LangGraph——LangChain用来构建稳健AI代理的框架来建一个。
我特别喜欢LangGraph的一点在于,它能让你把智能代理的思维和行动绘制为一个图。每个节点代表一种能力,比如搜索网络或编写代码,而连接节点的边则控制信息的流动。
当我开始构建代理时,这种方法让我觉得很好用,因为我可以直观地看到我的代理在思考的过程。
你的第一个助手:文章分析助手我们来看看如何使用LangGraph来创建一个文本分析工具。
这个智能代理会阅读文章,理解内容,提取关键信息,并生成简洁的摘要——就像你的私人研究助手。
开始搭建环境首先,你得先搭建你的开发环境。
首先 — 创建项目文件夹:
创建一个名为ai_agent_project的目录并进入该目录。
第二步 — 创建并启动虚拟环境:
请运行以下命令来在 Windows 中
python -m venv agent_env && agent_env\\Scripts\\activate
请运行以下命令来在 macOS/Linux 中
python3 -m venv agent_env
source agent_env/bin/activate # 在 macOS/Linux 中使用 source 命令激活虚拟环境
步骤3—安装所需的包:
pip install langgraph langchain langchain-openai python-dotenv
步骤4— 配置您的OpenAI API密钥:
我在使用GPT-4o mini(作为我们代理的“大脑”), 但你可以换成你喜欢的任何大型语言模型。如果没有API密钥的话:
- 在OpenAI上创建一个账户
- 找到API密钥部分
- 单击“生成新密钥(秘密密钥)”
- 复制你的API密钥(或复制你的密钥)
第5步—创建一个名为.env的文件:
# 在 Windows 系统中
echo OPENAI_API_KEY=你的API密钥here > .env
# macOS/Linux
echo "OPENAI_API_KEY=你的API密钥here" > .env
将 ‘这里的API密钥’ 替换为你的 OpenAI 的 API 密钥。
第六步 — 创建名为 test_setup.py
的测试文件
python
import os
from dotenv import load_dotenv
from langchain_openai import ChatOpenAI
# 加载环境变量
load_dotenv()
# 初始化 ChatOpenAI 实例
llm = ChatOpenAI(model="gpt-4o-mini")
# 测试一下设置
response = llm.invoke("Hello! Are you working?")
print(response.content)
如下:
python
import os
from dotenv import load_dotenv
from langchain_openai import ChatOpenAI
# 加载环境变量
load_dotenv()
# 初始化 ChatOpenAI 实例
llm = ChatOpenAI(model="gpt-4o-mini")
# 测试一下设置
response = llm.invoke("Hello! Are you working?")
print(response.content)
步骤7—进行测试。
运行测试设置脚本
python test_setup.py
(这是测试设置的Python脚本)
如果你收到回复,那你就可以恭喜你了,你就可以开始创建代理了。
让我们来创建我们的第一个代理程序首先,我们需要先导入必要的库:
import os # 导入操作系统模块
from typing import TypedDict, List # 导入类型定义和列表类型
from langgraph.graph import StateGraph, END # 导入状态图和结束标记
from langchain.prompts import PromptTemplate # 导入提示模板
from langchain_openai import ChatOpenAI # 导入聊天OpenAI模块
from langchain.schema import HumanMessage # 导入人类消息,这里可以理解为用户消息(用户消息更贴近实际语境)
StateGraph
管理代理组件间的信息流动。PromptTemplate
生成一致的指令,并使用ChatOpenAI
连接到OpenAI的聊天模型来驱动代理的思考过程。
我们的代理程序需要内存来追踪进度,我们可以用一个 TypedDict 来创建内存。
# 原始的问题或任务
class State(TypedDict): text =
# 跟踪代理的思考和决定
str classification =
# 存储工具产生的中间数据
str entities: List[str], summary: str
这种结构让我们的代理记住您的请求,跟踪它的推理过程,存储工具数据,并准备最终答案。使用 TypeDict
可以确保类型安全,如果存储了不正确的数据类型,会给我们警告,这使得调试变得简单。
既然我们的智能代理能记住东西了,咱们给它点思考的能力吧。
# 初始化为gpt-4o-mini模型,temperature设为0
# 一个基于ChatOpenAI的聊天模型
# llm 是加载的模型实例
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
将温度设置为0可以确保我们的代理始终选择最可能的响应——这对于遵循特定推理模式的代理特别重要。作为复习,温度可以作为LLM的“创意调节器”。
- 当温度0时:专注而确定的回应
- 当温度1时:更具创造性的输出
- 当温度2时:更狂野,有时混乱的想法
如果你的程序做了奇怪的决定,先看看你的温度设置是否正确。
代理的技能 不添加任何其他内容,仅根据要求修改翻译部分。现在我们将为我们的代理构建专门工具,每个工具专门处理特定类型的任务。
首先,咱们来看一下我们的分类能力:
def classification_node(state: State):
"""
将文本分类到以下预定义类别之一。
参数:
state (State): 包含要分类的文本的当前状态字典
返回值:
dict: 包含分类结果的字典
类别:
- 新闻: 当前事件的事实报道
- 博客: 个人或非正式的网络写作
- 研究: 学术或科学内容
- 其他: 不符合以上类别的内容
"""
# 定义一个提示模板,用于分类给定的文本
prompt = PromptTemplate(
input_variables=["text"],
template="将以下文本分类为以下类别之一:新闻、博客、研究或其它。\n\n文本:{text}\n\n类别:"
)
# 用状态中的文本格式化提示
message = HumanMessage(content=prompt.format(text=state["text"]))
# 调用语言模型,根据提示进行文本分类
classification = llm.invoke([message]).content.strip()
# 返回分类结果字典
return {"classification": classification}
此功能利用提示模板向我们的AI模型下达明确的指示。该功能接收我们当前的状态信息,包含我们要分析的文本内容,并给出其分类结果。
接下来,我们将展示我们的实体提取能力。
def 实体提取函数(state: State):
# 用于从文本中识别并提取命名实体(如人名、组织名和地名)的函数
# 按类别(人名、组织名、地名)组织
# 创建实体提取提示模板,指定要查找的实体及其格式(以逗号分隔)
prompt = PromptTemplate(
input_variables=["text"],
template="从以下文本中提取所有的实体(人名、组织名、地名)。提供结果以逗号分隔的列表。\n\n文本:{text}\n\n实体:"
)
# 使用来自state的文本格式化提示,并包装在HumanMessage中
message = HumanMessage(content=prompt.format(text=state["text"]))
# 发送到语言模型,获取响应,清理空白字符并拆分为列表
entities = llm.invoke([message]).content.strip().split(", ")
# 返回包含实体列表的字典,以合并到智能体的状态中
return {"entities": entities}
这个功能处理文档,并返回一个包含重要实体(例如人名、组织和地点)的列表。
最后,我们来介绍一下我们的总结功能。
def summarize_node(state):
# 创建一个用于总结提示的模板
# 这提示模型用一句话总结输入文本
summarization_prompt = PromptTemplate.from_template(
"""请用一句话总结以下文本。
文本: {input}
概要:"""
)
# 通过将提示模板和语言模型连接起来来创建一个链
# “|”运算符将提示输出传递给模型
chain = summarization_prompt | llm
# 使用状态字典中的输入文本来执行链
# 这将要被总结的文本传递给模型
response = chain.invoke({"input": state["input"]})
# 返回一个从模型响应中提取的摘要字典
# 这将被合并到代理的状态中
return {"summary": response.content}
这个功能可以将文件的主要内容提炼成简洁明了的总结。
这些技能加起来使我们的代理能够理解内容类型,识别关键信息,并生成易于消化的摘要——每个功能都按照相同的步骤操作:获取手头的状态,处理一下,然后提供有用的信息给下一个功能。
完成代理架构现在我们将把这些能力整合成一个协调的工作流程。
workflow = StateGraph(状态)
# 添加节点到图中
workflow.add_node("classification_node", 分类节点)
workflow.add_node("entity_extraction", 实体提取节点)
workflow.add_node("summarization", 摘要节点)
# 添加边到图中
workflow.set_entry_point("classification_node") # 设置图的入口点,其中END表示结束点
workflow.add_edge("classification_node", "entity_extraction")
workflow.add_edge("entity_extraction", "summarization")
workflow.add_edge("summarization", END)
# 编译工作流图
app = workflow.compile()
恭喜啦!
你创建了一个代理,它能够从分类到实体提取再到摘要生成,有序地进行,它能理解文本类型,并识别重要实体,生成摘要内容,最后完成整个流程。
如下是LangGraph Agent的简介!
现在我们用一段样文来试一试我们的智能助手吧。
# 定义一个关于Anthropic的MCP的示例文本,用于测试我们的代理
sample_text = """
Anthropic的MCP(模型上下文协议,Model Context Protocol)是一个强大的开源工具,它可以让您的应用程序轻松地与不同系统中的API进行交互。
"""
# 使用我们的示例文本创建初始状态
state_input = {"text": sample_text}
# 在示例文本上运行代理的完整工作流程
result = app.invoke(state_input)
# 打印结果的每个组件:
# - 分类:新闻、博客、研究或其他
print("分类:", result["classification"])
# - 提取的实体:人名、组织和地点
print("\n实体:", result["entities"])
# - 文本的摘要
print("\n摘要:", result["summary"])
运行这段代码会依次将文本通过每个步骤进行处理
- 分类:技术
- 实体名称:[‘Anthropic’, ‘MCP’, ‘Model Context Protocol’]
- 简介:MCP,由Anthropic开发,是一个开源协议,使应用程序能够与各种API系统实现无缝交互。
不仅令人印象深刻的是最终的结果,更重要的是,每一个阶段都是在前一个基础上构建起来的。这与我们自己的阅读步骤相似:我们首先确定内容类型,接着识别重要的名称和概念,最后,我们会在心中创建一个将所有内容联系起来的总结。
这种方法不仅限于我们的技术示例。你可以类似地应用于:
- 个人成长文章 — 划分成长领域,提炼出有用的建议,归纳出关键点
- 创业者经历 — 了解商业模式、融资模式和增长方式
- 产品评价 — 识别功能、品牌和推荐产品
我们的代理在我们设计的严格节点和连接点网络中工作。
这种可预测性限制了它的灵活性。与人类不同,代理走固定的路,在遇到意外情况时无法灵活调整。
上下文理解是另一个限制。这个代理只能处理给出的文本,但缺乏人类自然而然掌握的广泛知识和文化细节。不过,如果增加互联网搜索功能,可以增强它的知识量。这个代理只能在给定的文本范围内运作。
黑箱问题也存在于代理机制中。我们能看到输入和输出,但看不到内部的决策过程,就像黑箱子一样。像GPT-o1或DeepSeek R1这样的模型通过展示它们的思考过程提供了更多的透明度,让内部过程更加清晰,尽管我们依然无法完全控制其内部运作。
最后,这些系统并非完全自主,需要人工监管,尤其是在验证输出和确保准确性时。就像其他任何AI系统一样,最佳结果通常来自结合AI能力和人工监管。
了解这些限制的边界有助于我们打造更优秀的系统,并明确何时需要人类干预。最佳的结果来自于将AI的能力与人类的专长结合起来。
我那次代理演示失败的惨痛教训回想那次尴尬的客户演示失败,我现在才明白理解代理的局限性是成功的关键。我忽略了代理架构的根本限制,结果系统过于复杂,导致系统崩溃。
通过接受代理需要四个方面:(1) 明确的框架来运作,(2) 定义的路径,(3) 部分黑盒方式运作,(4) 人类监督,我建立了能够产出结果的系统,而不是陷入无尽的澄清漩涡。
那次痛苦的演示教会了我 AI开发中最宝贵的一课:往往构建非凡事物的路径始于了解AI不能做什么。
理解这些限制并不会削弱代理技术——相反,它让代理技术真正有用。而这正是一个会崩溃的演示和一个能够带来实际效果的解决方案的区别。
想常常听我说话吗?我每日分享实用建议、技巧和最新资讯,帮助你避免昂贵的错误,并保持在人工智能领域的前沿。关注我:
你是不是技术专业人士,希望通过写作来扩大你的读者群?👉 别错过我的电子报!
我的科技受众加速器包含了实用的文案写作和受众拓展策略,这些策略已经帮助数百位专业人士脱颖而出并加速他们的职业发展。立即订阅,紧跟潮流:
共同學習,寫下你的評論
評論加載中...
作者其他優質文章