1. 项目概述:从“执行”到“思考”的跨越
最近和几个做产品的朋友聊天,大家都有一个共同的感受:现在市面上的AI应用,无论是聊天机器人还是自动化工具,大多还停留在“你问我答”或“你指令我执行”的层面。它们很强大,能写代码、画图、分析数据,但总觉得缺了点“灵性”——就像一个反应迅速但缺乏主观能动性的助手。这让我萌生了一个想法:能不能自己动手,打造一个真正会“思考”的AI智能体?不是那种简单地调用API返回结果,而是能自主规划、分解任务、调用工具、并从结果中学习调整的智能体。
这个“手撸一个会‘思考’的AI智能体”的项目,核心目标就是构建一个具备初步自主决策能力的AI Agent。它不再是一个被动的响应者,而是一个主动的问题解决者。想象一下,你给它一个模糊的目标,比如“帮我分析一下上个月的销售数据,找出问题并给出下个月的优化建议”。一个传统的脚本或简单的API调用可能就卡壳了,因为它不知道具体要分析什么、用什么工具、步骤是什么。但一个会“思考”的智能体,应该能自己拆解这个目标:第一步,需要获取销售数据(可能调用数据库API或读取本地文件);第二步,进行数据清洗和预处理(调用Pandas库);第三步,进行趋势分析和异常检测(可能结合统计分析或简单的机器学习);第四步,基于分析结果生成可视化图表(调用Matplotlib);第五步,综合所有信息,用自然语言撰写一份分析报告。这个过程,就是“思考”的体现。
这个项目非常适合有一定Python基础,并对大模型应用开发感兴趣的开发者。你不需要是机器学习专家,但需要对如何将大模型的能力与编程逻辑结合有浓厚的兴趣。通过这个项目,你将深入理解智能体的核心架构——ReAct(Reasoning and Acting)、工具调用(Tool Calling)、记忆(Memory)和规划(Planning)等概念,并亲手用代码将它们实现出来。我们将使用目前公认最易上手且功能强大的开发框架之一,结合一个主流的大模型API(如DeepSeek、OpenAI等),从零开始搭建。你会发现,让AI“思考”起来,其内核是一套精巧的流程设计和提示词工程,而代码则是实现这套设计的骨架。
2. 智能体的核心架构与设计思路
要构建一个会“思考”的智能体,首先得弄明白“思考”在代码层面意味着什么。它不是一个玄学概念,而是一系列可设计、可实现的模块协同工作的结果。经过业界多年的探索,一个功能完备的智能体通常包含以下几个核心组件,我们的项目也将围绕它们展开。
2.1 大脑:大语言模型与提示词工程
智能体的“大脑”无疑是大语言模型。它负责理解用户指令、进行逻辑推理、生成决策和自然语言回应。但直接给模型一个复杂问题,它往往无法给出可执行的方案。这时就需要“提示词工程”来引导它的思考过程。
这里的关键是设计一套系统提示词,它定义了智能体的角色、能力范围、思考格式和行动规范。例如,我们的提示词会明确告诉模型:“你是一个AI智能体,可以调用各种工具解决问题。请遵循以下格式思考:Thought: (分析当前情况和下一步该做什么),Action: (需要调用的工具名称),Action Input: (调用该工具所需的输入参数)。当你得到工具的执行结果后,再继续思考。” 这种结构化的提示,强制模型进行一步步的推理,并将思考过程与执行动作分离,这是我们实现“思考”可视化和可控化的基础。
选择大模型API时,我们需要考虑其推理能力、对工具调用格式的支持、上下文长度以及成本。例如,DeepSeek-V4-Pro或DeepSeek-V4-Flash在推理和代码能力上表现突出,且提供了清晰的API。在代码中,我们会通过API密钥来初始化模型客户端,并确保请求格式符合其规范,避免出现类似‘type‘ must be in [“enabled“, “disabled“, “auto“]或maximum context length超限这类常见错误。
2.2 记忆模块:短期记忆与长期记忆
一个只会处理当前对话的智能体是健忘的,算不上真正的思考者。记忆模块让智能体拥有“上下文”和“经验”。
- 短期记忆(Conversation Memory):通常指对话历史。我们将使用“对话缓冲区记忆”,它保存了最近几轮的用户输入、智能体的思考(Thought)、行动(Action)和观察结果(Observation)。这保证了智能体在连续对话中能理解指代关系,比如用户说“把刚才分析的那个图表再解释一下”,智能体能知道“刚才”指的是什么。
- 长期记忆(Vector Store):对于更复杂的需求,比如让智能体记住自己的操作手册、特定领域知识或历史任务总结,我们需要向量数据库。将文本信息通过嵌入模型转化为向量并存储,当遇到相关问题时,智能体可以快速检索这些知识来辅助决策。例如,我们可以把“如何生成销售报表”的步骤文档存入向量库,当用户提出类似需求时,智能体能自动检索并参考。
在实现上,短期记忆可以通过一个简单的列表或队列在内存中维护。而长期记忆则需要引入像ChromaDB、FAISS这样的轻量级向量数据库,并结合一个嵌入模型API(如OpenAI的text-embedding-3-small)。
2.3 工具集:智能体的“手脚”
思考之后需要行动,工具就是智能体的手脚。一个智能体的能力边界,很大程度上取决于它拥有什么工具。在我们的项目中,工具可以是:
- 搜索工具:调用搜索引擎API获取实时信息。
- 计算器/代码执行工具:在一个安全的沙箱环境中执行Python代码,进行数学计算或数据处理。
- 文件读写工具:读取本地文本文件、CSV数据,或将结果保存到文件。
- 专用API工具:调用天气查询、股票数据、翻译等第三方服务。
每个工具都需要被明确定义:工具名称、描述、输入参数(JSON Schema格式)。智能体的大脑(LLM)根据当前思考,决定调用哪个工具,并生成符合该工具要求的输入参数。在代码中,我们会将每个工具封装成一个Python函数,并提供一个统一的“工具注册表”供智能体查询和调用。
2.4 控制流:ReAct循环与规划器
这是将以上所有模块串联起来的“神经系统”,也是“思考”流程的核心体现。最经典的范式是ReAct(Reason + Act)循环。
- 观察:智能体接收用户的初始输入或上一个工具的执行结果。
- 思考:智能体结合当前观察、记忆中的历史信息和可用工具列表,分析现状,决定下一步是直接回答用户还是调用某个工具。这一步的输出就是结构化的
Thought。 - 行动:如果决定调用工具,则输出
Action和Action Input。我们的程序会解析这个输出,找到对应的工具函数并执行。 - 观察:获取工具执行的结果(或错误信息),作为新的“观察”输入给智能体,进入下一轮循环。
这个循环会一直持续,直到智能体在“思考”步骤中认为任务已经完成,并输出最终的Final Answer。
对于更复杂的任务,我们还需要一个规划器。比如,面对“分析销售数据”这种宏大目标,智能体可能需要先将其分解为“获取数据”、“清洗数据”、“分析趋势”、“生成报告”等子任务,然后为每个子任务启动一个ReAct循环。规划器可以是一个更高级的LLM调用,专门用于任务分解和排序。
3. 环境搭建与核心依赖配置
工欲善其事,必先利其器。在开始编码之前,我们需要一个干净、可复现的Python开发环境。这里我强烈推荐使用conda或venv创建虚拟环境,避免包版本冲突。
3.1 Python环境与IDE准备
首先,确保你的系统安装了Python 3.9或更高版本。你可以从Python官网下载安装。对于IDE,VSCode是一个绝佳的选择,配合Python插件和Pylance语言服务器,能获得很好的代码提示和调试体验。
在项目根目录下,创建并激活虚拟环境:
# 使用 venv python -m venv venv # 在Windows上激活 venv\Scripts\activate # 在Mac/Linux上激活 source venv/bin/activate激活后,你的命令行提示符前会出现(venv)字样,表示你已进入该虚拟环境。
3.2 依赖包安装与关键版本锁定
接下来,创建requirements.txt文件,并填入我们项目所需的核心依赖。这里的选择基于当前(2024年)最稳定和流行的智能体开发库。
# 核心LLM交互与智能体框架 langchain==0.1.0 langchain-community==0.0.10 # 我们使用OpenAI格式的API,兼容DeepSeek等众多模型 langchain-openai==0.0.5 # 向量数据库(用于长期记忆) chromadb==0.4.22 # 文本嵌入模型(用于将知识转化为向量) langchain-openai # 同样包含embeddings # 用于生成结构化输出(如Thought/Action),这对工具调用至关重要 langchain-core==0.1.0 # 可选但推荐:用于更优雅地管理环境变量 python-dotenv==1.0.0 # 基础工具包 requests==2.31.0 # 用于构建自定义API工具 pandas==2.1.0 # 数据处理工具示例使用pip安装:
pip install -r requirements.txt注意:
langchain及其生态版本迭代很快,上述版本号是撰写本文时的稳定组合。直接安装最新版可能会遇到接口不兼容的问题。如果你遇到ImportError或AttributeError,首先检查版本是否匹配。锁定版本是保证项目可复现的关键一步。
3.3 API密钥配置与安全管理
我们的智能体需要调用大模型API,因此需要配置API密钥。绝对不要将密钥硬编码在代码中!标准做法是使用环境变量。
- 在项目根目录创建
.env文件。 - 在
.env文件中填入你的API密钥,例如使用DeepSeek:
如果你使用OpenAI,则对应是DEEPSEEK_API_KEY=your_deepseek_api_key_here DEEPSEEK_API_BASE=https://api.deepseek.com # DeepSeek的API基础地址OPENAI_API_KEY。 - 在Python代码中,使用
python-dotenv加载配置:from dotenv import load_dotenv import os load_dotenv() # 加载 .env 文件中的环境变量 deepseek_api_key = os.getenv("DEEPSEEK_API_KEY") api_base = os.getenv("DEEPSEEK_API_BASE")
这样,你的密钥就与代码分离了。记得将.env文件添加到.gitignore中,避免意外提交到公开仓库。
4. 核心模块实现:打造智能体的“器官”
环境就绪后,我们开始动手实现智能体的各个核心模块。我会从最简单的工具开始,逐步组装成完整系统。
4.1 工具库的构建与封装
工具是智能体能力的延伸。我们先实现两个基础但强大的工具:网络搜索和Python代码执行。
工具一:DuckDuckGo搜索工具我们利用duckduckgo-search这个包来实现无需API密钥的搜索。 首先安装它:pip install duckduckgo-search。 然后封装成LangChain可识别的工具格式:
from langchain.tools import Tool from duckduckgo_search import DDGS def search_duckduckgo(query: str) -> str: """使用DuckDuckGo搜索网络信息。""" try: with DDGS() as ddgs: # 获取最相关的5条结果 results = [r for r in ddgs.text(query, max_results=5)] if not results: return "未找到相关信息。" # 将结果格式化为字符串 formatted_results = "\n\n".join([ f"标题:{r['title']}\n摘要:{r['body']}\n链接:{r['href']}" for r in results ]) return f"搜索到以下信息:\n{formatted_results}" except Exception as e: return f"搜索过程中出现错误:{str(e)}" # 创建Tool对象,定义名称、描述和函数 search_tool = Tool( name="Web_Search", func=search_duckduckgo, description="当需要获取最新的、实时的或未知领域的信息时使用此工具。输入一个搜索查询词。" )工具二:Python代码执行工具(安全沙箱)让智能体直接执行Python代码非常强大,但也极其危险。切勿在生产环境中直接使用exec()。这里我们使用langchain社区提供的PythonREPLTool,它在某种程度上提供了隔离。
from langchain_community.tools import PythonREPLTool python_repl_tool = PythonREPLTool( name="Python_REPL", description="执行Python代码并返回结果。适用于数学计算、数据转换、字符串处理等。输入一段有效的Python代码。" )重要警告:即使在开发中,也要谨慎使用此工具,避免执行删除文件、访问网络等危险代码。最好限制其可访问的模块(如禁用
os,sys等)。
工具三:自定义计算器对于简单的算术,我们可以做一个更安全的专用工具:
import ast import operator as op # 支持的操作符 allowed_operators = {ast.Add: op.add, ast.Sub: op.sub, ast.Mult: op.mul, ast.Div: op.truediv} def safe_eval(expr: str) -> str: """安全地评估一个仅包含数字和基础运算符的数学表达式。""" try: node = ast.parse(expr, mode='eval').body def _eval(node): if isinstance(node, ast.Num): # Python 3.7及以下用 ast.Num return node.n elif isinstance(node, ast.Constant): # Python 3.8+ return node.value elif isinstance(node, ast.BinOp): left = _eval(node.left) right = _eval(node.right) return allowed_operators[type(node.op)](left, right) else: raise TypeError(f"不支持的表达式类型:{type(node)}") result = _eval(node) return str(result) except Exception as e: return f"计算错误:{str(e)}。请确保输入是纯数学表达式,如‘(3+5)*2‘。" calc_tool = Tool( name="Calculator", func=safe_eval, description="计算一个数学表达式的结果。输入如 ‘(3+5)*2‘ 的表达式。" )将这三个工具放入一个列表,就构成了我们智能体的初始工具箱:
tools = [search_tool, python_repl_tool, calc_tool]4.2 记忆系统的实现
接下来,我们实现短期记忆。LangChain提供了多种记忆后端,这里我们使用简单的ConversationBufferMemory。
from langchain.memory import ConversationBufferMemory # 创建记忆对象。memory_key定义了存储对话历史的变量名,return_messages=True确保返回的是消息列表格式。 memory = ConversationBufferMemory(memory_key="chat_history", return_messages=True) # 我们还需要一个单独的变量来存储“中间步骤”,即Thought/Action/Observation的记录,这对于ReAct循环至关重要。 agent_memory = ConversationBufferMemory(memory_key="agent_scratchpad", return_messages=True)这个memory对象会自动保存用户和AI的对话。而agent_memory则专门用于记录智能体在思考过程中的中间步骤,这些步骤会作为上下文的一部分输入给模型,帮助它了解自己已经做了什么。
4.3 大模型连接与智能体创建
现在,我们连接“大脑”。这里以DeepSeek API为例,因为它兼容OpenAI的接口格式,性价比高且能力强劲。
from langchain_openai import ChatOpenAI # 注意:虽然导入的是ChatOpenAI,但通过指定base_url,我们可以连接任何兼容OpenAI API格式的服务,如DeepSeek。 llm = ChatOpenAI( model="deepseek-chat", # 根据DeepSeek文档,这是正确的模型名。也可能是“deepseek-v4-pro” openai_api_key=deepseek_api_key, # 从环境变量读取 base_url=api_base, # 从环境变量读取,例如 "https://api.deepseek.com/v1" temperature=0.1, # 温度设低,让输出更确定、更遵循指令 streaming=False, # 非流式响应,简化处理 )有了LLM、工具和记忆,我们就可以创建智能体了。LangChain提供了高级的create_react_agent函数,它封装了ReAct逻辑和提示词模板。
from langchain.agents import create_react_agent, AgentExecutor from langchain.agents.react.agent import get_prompt # 1. 获取ReAct智能体的专用提示词模板 # 这个模板包含了指导模型进行思考-行动循环的详细指令。 prompt_template = get_prompt(tools) # 2. 创建ReAct智能体 # 它将llm、提示词模板和工具绑定在一起。 agent = create_react_agent(llm, tools, prompt_template) # 3. 创建智能体执行器 # 这是真正运行智能体的引擎,它负责处理循环、调用工具、管理记忆。 agent_executor = AgentExecutor( agent=agent, tools=tools, memory=agent_memory, # 使用专门记录中间步骤的记忆 verbose=True, # 设为True,会在控制台打印详细的思考过程,便于调试 handle_parsing_errors=True, # 当模型输出格式不符合预期时,尝试自动修复 max_iterations=10, # 限制最大循环次数,防止陷入死循环 early_stopping_method="generate", # 停止条件 )至此,一个具备基础思考能力、拥有三个工具、并带有记忆的AI智能体骨架就搭建完成了。agent_executor就是我们的主入口。
5. 运行、测试与迭代优化
让我们用一个复杂的任务来测试这个智能体,观察它如何“思考”。
5.1 首次测试与思考过程观察
# 测试查询 query = “请先搜索‘2024年奥运会举办地’,然后用计算器计算一下,如果一张门票平均100美元,卖出500万张门票,总收入是多少美元?最后用Python验证一下这个计算结果。” try: response = agent_executor.invoke({"input": query}) print("\n=== 智能体最终回答 ===") print(response["output"]) except Exception as e: print(f"执行过程中出现错误:{e}")当verbose=True时,你会在控制台看到类似以下的详细输出,这就是智能体的“思考链”:
> Entering new AgentExecutor chain... Thought: 用户给了我一个多步骤的任务。首先需要搜索2024年奥运会举办地,然后进行一个乘法计算,最后用Python验证。我应该按顺序执行。 Action: Web_Search Action Input: 2024年奥运会举办地 Observation: 搜索到以下信息: 标题:2024年夏季奥运会 - 维基百科 摘要:2024年夏季奥运会,正式名称为第三十三届夏季奥林匹克运动会,将于2024年7月26日至8月11日在法国巴黎举行。 链接:https://zh.wikipedia.org/... (更多结果...) Thought: 好的,举办地是法国巴黎。现在进行第二步计算。总收入 = 平均票价 * 售票数量 = 100美元 * 5,000,000。 Action: Calculator Action Input: 100 * 5000000 Observation: 500000000 Thought: 计算结果是500,000,000美元。现在需要用Python验证这个结果。我可以写一个简单的乘法脚本。 Action: Python_REPL Action Input: result = 100 * 5000000 print(f“验证结果:{result}美元”) Observation: 验证结果:500000000美元 Thought: 所有步骤都已完成。我可以整理最终答案了。 Final Answer: 根据搜索,2024年奥运会将在法国巴黎举行。通过计算,平均100美元的门票卖出500万张,总收入为500,000,000美元(五亿美元)。Python代码验证结果一致。 > Finished chain. === 智能体最终回答 === 根据搜索,2024年奥运会将在法国巴黎举行。通过计算,平均100美元的门票卖出500万张,总收入为500,000,000美元(五亿美元)。Python代码验证结果一致。看到吗?智能体自主规划了步骤(搜索->计算->验证),在每一步都进行了“思考”,选择了正确的工具,并传递了正确的参数。这就是“思考”的直观体现。
5.2 常见问题排查与调试技巧
在实际开发中,你肯定会遇到各种问题。下面是一个常见错误速查表:
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
API Error: 400 ‘type‘ must be in [“enabled“, “disabled“, “auto“] | 请求体中的参数不符合API规范。 | 检查初始化ChatOpenAI时传递的参数,移除或更正未知参数。确保model名称正确。 |
API Error: 400 This model‘s maximum context length is ... | 输入给模型的上下文(对话历史+当前问题)太长,超出了模型的令牌限制。 | 1. 使用ConversationSummaryMemory或ConversationBufferWindowMemory替代ConversationBufferMemory,只保留最近几轮或总结历史。2. 在 AgentExecutor中设置max_token_limit参数。 |
Unable to connect to API (ECONNRESET) | 网络连接问题,或API服务端不稳定。 | 1. 检查网络。 2. 增加请求超时时间: llm = ChatOpenAI(..., request_timeout=60)。3. 实现简单的重试逻辑。 |
| 智能体陷入死循环,不断重复相同动作 | 模型无法从工具返回的结果中提取有效信息来推进任务,或者任务本身无法完成。 | 1. 检查工具返回的结果是否清晰、格式是否易于模型理解。 2. 设置 AgentExecutor的max_iterations(如10)和early_stopping_method。3. 优化提示词,明确告诉模型在何种条件下应停止并给出最终答案。 |
模型不按格式输出Thought/Action | 提示词模板对模型的约束力不够,或者模型本身对结构化输出支持不佳。 | 1. 确保使用的get_prompt是专为ReAct设计的。2. 尝试降低 temperature到0.1以下。3. 考虑使用支持“函数调用”或“JSON模式”的新版模型和对应Agent类型(如 create_openai_tools_agent),格式更稳定。 |
| 工具调用时参数错误 | 模型生成的Action Input不符合工具函数定义的参数格式。 | 1. 在工具description中更清晰地描述输入格式,例如“输入一个搜索关键词字符串”。2. 在工具函数内部做好错误处理和类型转换,返回友好的错误信息给模型作为“Observation”。 |
调试心得:
- 从简到繁:先用一个工具(如计算器)测试,确保基础流程跑通,再逐步增加复杂工具。
- 善用
verbose=True:这是理解智能体内部决策过程最重要的窗口。通过观察“Thought”,你能知道模型是否真正理解了任务和工具。 - 优化工具描述:工具的
name和description是模型选择工具的唯一依据。描述要精准、无歧义,并说明输入格式。例如,“输入一个搜索查询词”比“输入查询”要好得多。 - 处理解析错误:
handle_parsing_errors=True是个救星,但有时模型输出完全混乱时,它也无能为力。这时可以捕获异常,在代码中给模型一个友好的错误提示作为新的“Observation”,让它重试。
5.3 功能增强与进阶探索
基础版本运行稳定后,我们可以考虑增强它:
增加长期记忆:集成ChromaDB,让智能体能够学习并记住你的个人文档、项目代码库。当用户问“我之前写的那个数据处理函数逻辑是什么?”时,智能体可以自动检索相关代码片段。
from langchain_community.vectorstores import Chroma from langchain_openai import OpenAIEmbeddings from langchain.text_splitter import RecursiveCharacterTextSplitter from langchain_community.document_loaders import TextLoader # 加载文档、分割、生成向量并存储 loader = TextLoader(“your_document.txt”) documents = loader.load() text_splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50) splits = text_splitter.split_documents(documents) vectorstore = Chroma.from_documents(documents=splits, embedding=OpenAIEmbeddings()) # 创建一个检索工具 retriever_tool = Tool( name=“Document_Search”, func=lambda q: vectorstore.similarity_search(q, k=2), description=“从知识库中搜索与问题相关的文档片段。” ) # 将此工具加入到tools列表中实现子任务规划:对于“写一份行业分析报告”这种复杂指令,可以设计一个“规划器”智能体,先将任务分解为“搜集资料”、“整理数据”、“撰写大纲”、“润色成文”等子任务,然后由“执行器”智能体(即我们刚建的)逐个完成。
接入图形界面:使用
Gradio或Streamlit快速构建一个Web界面,让非技术用户也能与你的智能体对话。pip install gradioimport gradio as gr def chat_with_agent(message, history): response = agent_executor.invoke({“input”: message}) return response[“output”] gr.ChatInterface(chat_with_agent).launch()
构建一个会“思考”的AI智能体,就像在组装一个数字生命。从简单的工具调用到复杂的规划循环,每一步都让你更贴近AI应用开发的前沿。这个项目最大的收获不是最终的代码,而是在调试过程中对模型思维模式、提示词魔力以及系统工程设计的深刻理解。当你看到它第一次自主地、正确地完成一个多步骤任务时,那种成就感是无可比拟的。接下来,试着给它接入更多工具,比如发送邮件、操作日历、管理文件,你会发现,一个属于你的“贾维斯”正在慢慢成型。