最近在尝试将AI能力集成到自己的项目中时发现网上关于Agent和Skill的资料要么过于理论化要么就是零散的代码片段很难找到一个从核心概念到完整项目落地的系统教程。很多开发者卡在环境配置、工具链选择和代码调试环节反复踩坑。本文旨在提供一个闭环的实操方案手把手带你从零理解Agent与Skill的架构并完成一个可运行的智能体开发实战。无论你是想入门AI应用开发的学生还是希望将智能体能力集成到业务中的后端工程师都能从本文中找到清晰的路径和可复用的代码。1. Agent与Skill核心概念解析从理论到工程视角在开始敲代码之前我们必须先厘清几个核心概念。这能帮助我们在后续开发中做出正确的技术选型和架构设计。1.1 什么是Agent智能体在AI和软件工程的交叉领域Agent智能体通常被定义为一个能够感知环境、自主决策并执行行动以实现特定目标的软件实体。你可以把它想象成一个数字世界的“代理人”或“助手”。从工程实现角度看一个典型的Agent包含以下核心组件感知模块负责接收输入这可以是用户的自然语言指令、传感器的数据流、API的调用请求或数据库的查询结果。决策/推理模块这是Agent的“大脑”。它基于感知到的信息、内置的知识或模型以及预设的目标决定下一步该做什么。在现代AI Agent中大语言模型LLM常扮演核心推理引擎的角色。执行模块负责将决策转化为具体的行动。行动可以是调用一个工具Tool、执行一段代码、发送一个API请求或者生成一段自然语言回复。记忆模块用于存储对话历史、执行结果、学到的知识等使Agent具备上下文感知和持续学习的能力。与传统的脚本或程序不同Agent强调自主性和目标导向性。它不是为了执行一个固定流程而是为了在动态环境中努力达成一个目标。1.2 什么是Skill技能如果说Agent是“人”那么Skill技能就是这个人掌握的“本领”或“工具”。Skill是Agent能力的具体化和模块化。一个Skill定义了Agent能够完成的一个具体任务。例如查询天气Skill输入城市名输出天气信息。发送邮件Skill输入收件人、主题、内容执行发送操作。数据库查询Skill输入SQL语句返回查询结果。代码生成Skill输入需求描述输出代码片段。从开发角度看一个Skill通常包含以下部分技能描述用自然语言清晰定义这个技能是做什么的让Agent的“大脑”LLM能够理解何时该调用此技能。输入/输出参数定义技能执行所需的参数格式和返回的数据结构。执行函数一段具体的代码逻辑真正完成技能所描述的任务。它可以是调用第三方API、操作数据库、运行计算等。Agent与Skill的关系一个强大的Agent往往集成了多个Skills。Agent的“大脑”LLM根据用户请求和理解自主选择并组合调用一个或多个Skills来解决问题。这种架构使得Agent的能力可以像乐高积木一样被灵活扩展。1.3 主流开发框架与技术栈选择目前社区有多种Agent开发框架选择合适的可以事半功倍。以下是对比框架/库主要语言核心特点适用场景LangChainPython/JS生态最丰富概念全面Agent, Tool, Chain文档成熟。快速原型验证研究构建复杂的、多步骤的AI应用。LlamaIndexPython专注于数据连接和检索RAG其Agent能力也基于Tools。需要与私有数据、知识库深度结合的Agent应用。Semantic KernelC#/Python微软出品强调规划Planner和原生函数Native Functions。.NET生态集成企业级应用需要复杂任务规划的场景。AutoGenPython专注于多智能体对话和协作由微软研究团队开发。构建多个Agent相互对话、协作完成任务的系统。对于初学者和大多数应用场景从LangChainPython版开始是最佳选择。它提供了最直观的抽象社区支持最好能让我们快速抓住Agent开发的本质。本文的实战部分也将基于LangChain展开。2. 环境准备与项目初始化我们将创建一个独立的Python项目确保环境隔离避免依赖冲突。2.1 创建项目与虚拟环境打开你的终端命令行执行以下步骤# 1. 创建一个新的项目目录 mkdir ai-agent-tutorial cd ai-agent-tutorial # 2. 创建Python虚拟环境推荐使用Python 3.9 python -m venv venv # 3. 激活虚拟环境 # 在 macOS/Linux 上 source venv/bin/activate # 在 Windows 上 # venv\Scripts\activate # 激活后命令行提示符前通常会显示 (venv)2.2 安装核心依赖在项目根目录下创建一个requirements.txt文件并填入以下内容# 核心AI与Agent框架 langchain0.1.0 langchain-openai0.0.5 langchain-community0.0.10 # OpenAI官方SDK (用于调用GPT模型) openai1.6.1 # 用于发起网络请求许多Skill需要 requests2.31.0 # 用于解析HTML示例Skill会用到 beautifulsoup44.12.2 # 环境变量管理保护你的API密钥 python-dotenv1.0.0然后在激活的虚拟环境中安装它们pip install -r requirements.txt2.3 配置API密钥以OpenAI为例你需要一个大语言模型作为Agent的“大脑”。这里我们使用OpenAI的GPT模型。请前往 OpenAI平台 注册并获取API密钥。在项目根目录创建.env文件用于安全存储密钥# .env 文件 OPENAI_API_KEY你的实际API密钥重要安全提示切勿将.env文件提交到Git等版本控制系统。请将它添加到.gitignore文件中。本文所有代码都通过python-dotenv从环境变量读取密钥。2.4 项目结构预览完成后的基础项目结构如下ai-agent-tutorial/ ├── venv/ # Python虚拟环境目录.gitignore忽略 ├── .env # 环境变量文件.gitignore忽略 ├── .gitignore # Git忽略文件 ├── requirements.txt # 项目依赖列表 ├── simple_agent.py # 第一个简单Agent示例 ├── skill_agent.py # 集成自定义Skill的Agent └── README.md # 项目说明3. 第一个智能体你好世界让我们从一个最简单的Agent开始它不调用任何Skill仅仅是一个能和你对话的LLM。创建文件simple_agent.py# simple_agent.py import os from dotenv import load_dotenv from langchain_openai import ChatOpenAI from langchain.agents import initialize_agent, AgentType from langchain.memory import ConversationBufferMemory # 1. 加载环境变量中的API密钥 load_dotenv() # 2. 初始化大语言模型 (使用GPT-3.5-turbo成本较低适合实验) llm ChatOpenAI( modelgpt-3.5-turbo, temperature0, # 温度设为0使输出更确定、更稳定 openai_api_keyos.getenv(OPENAI_API_KEY) ) # 3. 初始化记忆让Agent能记住对话历史 memory ConversationBufferMemory(memory_keychat_history, return_messagesTrue) # 4. 初始化一个最简单的“零技能”Agent # AgentType.CHAT_CONVERSATIONAL_REACT_DESCRIPTION 适合对话场景 agent initialize_agent( tools[], # 工具Skill列表为空 llmllm, agentAgentType.CHAT_CONVERSATIONAL_REACT_DESCRIPTION, memorymemory, verboseTrue # 设置为True可以看到Agent的思考过程 ) # 5. 运行Agent进行对话 if __name__ __main__: print(你好我是一个简单的对话Agent。输入‘退出’来结束对话。) while True: user_input input(\n你: ) if user_input.lower() 退出: print(Agent: 再见) break response agent.run(user_input) print(fAgent: {response})代码解读与运行导入与配置加载密钥初始化LLM。temperature参数控制创造性0表示最稳定。记忆ConversationBufferMemory会保存整个对话历史让Agent有上下文。初始化Agentinitialize_agent是LangChain创建Agent的核心函数。即使tools[]它依然是一个能理解指令并回应的LLM。运行启动一个简单的对话循环。在终端运行它python simple_agent.py你会看到类似以下的输出verboseTrue会打印内部思考 进入新的AgentExecutor链... 思考用户问了一个简单的问题我可以用我的知识直接回答。 行动无可用工具我将直接回答。 观察无可用工具。 最终答案北京是中国的首都是一座历史悠久、文化丰富的城市。 你好我是一个简单的对话Agent。输入‘退出’来结束对话。 你: 北京是哪个国家的首都 Agent: 北京是中国的首都。这个Agent虽然简单但它已经具备了感知你的输入、决策LLM思考、执行生成回答和记忆记住对话的基本框架。接下来我们为它赋予真正的“技能”。4. 开发你的第一个自定义Skill一个没有技能的Agent就像没有双手的专家空有知识却无法行动。我们来创建两个实用的Skill一个获取实时天气一个进行网页内容摘要。4.1 技能一天气查询Skill我们将使用一个免费的天气API。这里以 Open-Meteo 为例它无需注册即可使用。创建文件weather_skill.py# weather_skill.py import requests from typing import Optional from langchain.tools import tool tool def get_current_weather(city_name: str) - str: 根据城市名称获取当前的天气情况。 Args: city_name: 城市的名称例如“北京”、“Shanghai”。 Returns: 一个描述当前天气的字符串包括温度、天气状况等。 如果查询失败返回错误信息。 try: # 第一步通过地理编码API获取城市的经纬度 # 这里使用NominatimOpenStreetMap的搜索服务请注意其使用条款。 geocode_url fhttps://nominatim.openstreetmap.org/search?city{city_name}formatjsonlimit1 headers {User-Agent: MyAgentApp/1.0 (your-emailexample.com)} # 礼貌起见设置User-Agent geo_response requests.get(geocode_url, headersheaders) geo_data geo_response.json() if not geo_data: return f抱歉未找到城市 {city_name} 的地理信息。 lat geo_data[0][lat] lon geo_data[0][lon] # 第二步使用经纬度查询天气 weather_url fhttps://api.open-meteo.com/v1/forecast?latitude{lat}longitude{lon}current_weathertrue weather_response requests.get(weather_url) weather_data weather_response.json() current weather_data.get(current_weather, {}) temperature current.get(temperature) wind_speed current.get(windspeed) weather_code current.get(weathercode) # 简单映射天气代码到描述Open-Meteo的WMO代码 weather_map { 0: 晴空, 1: 基本晴朗, 2: 局部多云, 3: 阴天, 45: 雾, 48: 雾凇, 51: 小雨, 53: 中雨, 55: 大雨, 61: 小雨, 63: 中雨, 65: 大雨, 80: 小阵雨, 81: 中阵雨, 82: 大阵雨 } condition weather_map.get(weather_code, 未知天气状况) return f{city_name}的当前天气温度 {temperature}°C风速 {wind_speed} km/h天气状况{condition}。 except Exception as e: return f查询天气时出现错误{str(e)}关键点解析tool装饰器这是LangChain将普通Python函数标记为Agent可用“工具”即Skill的标准方式。文档字符串Docstring函数的文档字符串至关重要Agent的“大脑”LLM会阅读这个描述来决定是否以及如何调用这个工具。描述必须清晰准确。类型提示city_name: str和- str有助于框架理解输入输出。错误处理Skill必须健壮。使用try-except捕获异常并返回友好的错误信息避免整个Agent崩溃。4.2 技能二网页摘要Skill创建文件web_summary_skill.py# web_summary_skill.py import requests from bs4 import BeautifulSoup from langchain.tools import tool from langchain_openai import ChatOpenAI import os from dotenv import load_dotenv load_dotenv() tool def summarize_webpage(url: str) - str: 获取给定URL的网页内容并使用AI生成一个简洁的摘要。 Args: url: 需要摘要的网页URL。 Returns: 网页内容的摘要文本。如果无法获取或处理网页则返回错误信息。 try: # 1. 获取网页内容 headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 } response requests.get(url, headersheaders, timeout10) response.raise_for_status() # 检查HTTP错误 # 2. 解析HTML提取主要文本 soup BeautifulSoup(response.content, html.parser) # 移除脚本、样式等无关标签 for script in soup([script, style, nav, footer, header]): script.decompose() text soup.get_text(separator , stripTrue) # 3. 简单裁剪过长的文本避免超出模型token限制 if len(text) 3000: text text[:3000] ... [内容已截断] if not text or len(text) 50: return 从该网页提取到的文本内容过少无法生成有效摘要。 # 4. 调用LLM生成摘要 llm ChatOpenAI( modelgpt-3.5-turbo, temperature0, openai_api_keyos.getenv(OPENAI_API_KEY) ) prompt f请为以下网页内容生成一个简洁的摘要不超过200字\n\n{text} summary_result llm.invoke(prompt) return f网页摘要{summary_result.content} except requests.exceptions.RequestException as e: return f网络请求失败无法获取网页{str(e)} except Exception as e: return f处理网页时发生未知错误{str(e)}关键点解析组合技能这个Skill内部又调用了LLM展示了Skill可以很复杂甚至可以嵌套其他AI能力。网页解析使用BeautifulSoup清理HTML提取核心文本内容这是处理非结构化数据的常见步骤。Token限制LLM有上下文长度限制对过长的网页内容进行截断是必要的工程处理。超时与异常网络请求必须设置超时并对各种异常情况进行妥善处理。5. 构建多功能智能体集成与实战现在我们将两个自定义Skill集成到Agent中并观察它如何自主决策和使用工具。创建主文件skill_agent.py# skill_agent.py import os from dotenv import load_dotenv from langchain_openai import ChatOpenAI from langchain.agents import initialize_agent, AgentType from langchain.memory import ConversationBufferMemory # 导入我们自定义的技能 from weather_skill import get_current_weather from web_summary_skill import summarize_webpage # 加载环境变量 load_dotenv() def main(): # 1. 初始化LLM llm ChatOpenAI( modelgpt-3.5-turbo, temperature0, openai_api_keyos.getenv(OPENAI_API_KEY) ) # 2. 准备工具Skill列表 tools [get_current_weather, summarize_webpage] # 3. 初始化记忆 memory ConversationBufferMemory(memory_keychat_history, return_messagesTrue) # 4. 初始化具有工具的Agent # 使用 STRUCTURED_CHAT_ZERO_SHOT_REACT_DESCRIPTION它对工具调用有更好的结构化支持 agent initialize_agent( toolstools, llmllm, agentAgentType.STRUCTURED_CHAT_ZERO_SHOT_REACT_DESCRIPTION, memorymemory, verboseTrue, # 强烈建议开启以观察Agent的思考链 handle_parsing_errorsTrue # 当Agent输出格式错误时尝试自动修复 ) print(多功能智能体已启动我可以查询天气和总结网页。输入‘退出’来结束。) print(- * 50) # 5. 交互循环 while True: try: user_input input(\n你: ) if user_input.lower() in [退出, exit, quit]: print(Agent: 感谢使用再见) break # 运行Agent传入用户输入 response agent.invoke({input: user_input}) print(f\nAgent: {response[output]}) except Exception as e: # 捕获并打印可能出现的错误避免程序崩溃 print(f\n系统出现错误: {str(e)}) print(Agent可能不理解指令或工具调用出错请换一种方式提问。) if __name__ __main__: main()运行与演示在终端运行python skill_agent.py。由于设置了verboseTrue你将看到Agent完整的“思考-行动-观察”循环。示例对话1查询天气你: 上海现在的天气怎么样 进入新的AgentExecutor链... 思考用户想了解上海的当前天气。我有一个工具叫get_current_weather可以用来查询城市的天气。我需要使用这个工具。 行动{ action: get_current_weather, action_input: {city_name: 上海} }观察上海的当前天气温度 22.5°C风速 15.3 km/h天气状况局部多云。 思考我已经通过工具获取了上海的天气信息现在可以回答用户了。 最终答案上海现在的天气是局部多云温度大约22.5摄氏度风速15.3公里每小时。 Agent: 上海现在的天气是局部多云温度大约22.5摄氏度风速15.3公里每小时。示例对话2总结网页你: 请帮我总结一下这个网页的内容 https://example.com/blog/ai-trends 进入新的AgentExecutor链... 思考用户提供了一个URL希望我总结网页内容。我有一个工具叫summarize_webpage正是做这个的。我需要调用它。 行动{ action: summarize_webpage, action_input: {url: https://example.com/blog/ai-trends} }观察网页摘要该文章讨论了2024年人工智能的三大趋势多模态AI的成熟、Agentic AI智能体的兴起以及AI在科学发现中的加速应用。文章指出AI正从被动工具转向主动协作伙伴。 思考我已经得到了网页的摘要可以直接将这个摘要回复给用户。 最终答案根据摘要该网页主要讨论了2024年AI的三大趋势1. 多模态AI成熟2. 智能体Agentic AI兴起3. AI加速科学发现。文章认为AI正在向主动协作伙伴演变。 Agent: 根据摘要该网页主要讨论了2024年AI的三大趋势...示例对话3复杂任务组合技能你: 我想先去北京旅游查一下天气然后了解一下故宫的官网介绍。 进入新的AgentExecutor链... 思考用户提出了两个请求1. 查询北京天气2. 了解故宫官网介绍。第二个请求隐含了需要总结故宫官网网页。我有两个工具可以分别处理。 我需要先查询北京天气。 行动{ action: get_current_weather, action_input: {city_name: 北京} }观察北京的当前天气温度 18°C风速 10 km/h天气状况晴空。 思考我已经得到了北京的天气。现在需要处理第二个请求。用户提到了“故宫的官网”我需要一个具体的URL。我应该先询问用户故宫官网的具体网址或者假设一个常用网址如 www.dpm.org.cn。为了更准确我先询问网址。 最终答案北京目前天气晴朗温度18°C风速10km/h适合出行。关于故宫官网的介绍请问您指的是哪个具体的网址例如是否是“https://www.dpm.org.cn” Agent: 北京目前天气晴朗...接着会等待你提供URL通过verbose输出你可以清晰地看到Agent的“思考链”ReAct模式思考要做什么行动调用哪个工具并传入什么参数观察工具返回的结果然后基于结果进行下一步思考或给出最终答案。这正是智能体自主性的体现。6. 常见问题与深度排查指南在实际开发中你一定会遇到各种问题。以下是高频问题及其解决方案。6.1 Agent不调用工具总是直接回答问题现象无论你怎么问Agent都只用LLM的知识直接生成答案而不去调用你提供的Skill。可能原因与解决方案工具描述不清晰检查Skill函数的文档字符串。描述必须极其精确说明工具的用途、输入和输出。LLM根据描述决定是否调用。尝试重写描述使其更匹配你的问题。Agent类型选择不当AgentType.CHAT_CONVERSATIONAL_REACT_DESCRIPTION更偏向对话可能不积极调用工具。尝试换成AgentType.STRUCTURED_CHAT_ZERO_SHOT_REACT_DESCRIPTION或AgentType.ZERO_SHOT_REACT_DESCRIPTION。LLM能力或指令问题在初始化LLM时可以通过system_message或prompt加强指令。例如from langchain.prompts import ChatPromptTemplate, SystemMessagePromptTemplate system_template “你是一个强大的助手必须优先使用提供的工具来回答问题。只有在工具无法处理时才用你自己的知识回答。” prompt ChatPromptTemplate.from_messages([ SystemMessagePromptTemplate.from_template(system_template), MessagesPlaceholder(variable_namechat_history), HumanMessagePromptTemplate.from_template({input}), MessagesPlaceholder(variable_nameagent_scratchpad) ]) # 然后将这个prompt用于创建Agent问题表述太泛尝试更具体、更指令化地提问。例如将“今天热吗”改为“使用天气工具查询一下北京今天的温度”。6.2 工具调用参数错误或格式不符问题现象Agent决定调用工具但调用时参数错误导致工具执行失败。在verbose日志中你会看到Observation: Error ...。解决方案强化类型提示和描述确保Skill函数的参数有明确的类型提示如str,int和在文档字符串中有详细说明。使用StructuredTool对于参数复杂的工具可以使用StructuredTool.from_function它能生成更规范的schema供LLM理解。from langchain.tools import StructuredTool weather_tool StructuredTool.from_function( funcget_current_weather, nameGetCurrentWeather, description获取指定城市的当前天气信息。, args_schemaWeatherInputSchema # 可以定义一个Pydantic模型来定义参数 )开启handle_parsing_errorsTrue正如我们在主代码中设置的这个参数能让Agent在工具调用格式出错时尝试自我修正而不是直接崩溃。6.3 网络超时、API限制或密钥错误问题现象工具执行过程中抛出requests.exceptions.Timeout,ConnectionError或API返回权限错误。排查清单超时在requests.get()中增加timeout参数如timeout10。代理问题如果你在网络受限环境可能需要配置代理。注意此处仅讨论技术配置不涉及任何违规用途可以在代码中设置import os os.environ[HTTP_PROXY] http://your-proxy:port os.environ[HTTPS_PROXY] http://your-proxy:portAPI密钥确认.env文件中的密钥正确且已通过load_dotenv()加载。确认OpenAI账户有余额且API调用未超频。第三方API限制免费API通常有调用频率限制请遵守其条款并在代码中添加适当的延迟或错误重试逻辑。6.4 记忆混乱或上下文过长问题现象在长对话中Agent忘记之前的内容或者响应速度变慢、成本增高。解决方案选择记忆类型ConversationBufferMemory会保存所有历史可能导致上下文过长。可以换用ConversationBufferWindowMemory只保留最近K轮对话。from langchain.memory import ConversationBufferWindowMemory memory ConversationBufferWindowMemory(k5, memory_keychat_history, return_messagesTrue)使用对话摘要记忆ConversationSummaryMemory会定期让LLM总结对话历史从而压缩信息。from langchain.memory import ConversationSummaryMemory memory ConversationSummaryMemory(llmllm, memory_keychat_history)手动管理在复杂应用中可以考虑将关键信息如用户偏好、任务状态存入数据库而非全部依赖LangChain的内存机制。7. 最佳实践与项目进阶方向掌握了基础开发后遵循以下实践能让你的Agent项目更加健壮、可维护并探索更强大的能力。7.1 工程化最佳实践技能Tool设计原则单一职责一个Skill只做一件事并做好。这有利于复用和测试。强健性进行充分的输入验证和异常处理返回明确的错误信息。无状态性尽可能让Skill是无状态的执行结果只依赖于输入参数。状态应由Agent的记忆或外部存储管理。清晰文档文档字符串是Skill与Agent沟通的桥梁务必用自然语言写清楚功能、输入和输出示例。配置与密钥管理永远不要将API密钥硬编码在代码中。使用.env文件和环境变量。对于生产环境使用专业的密钥管理服务如AWS Secrets Manager, HashiCorp Vault。将模型类型、温度等参数也放入配置便于在不同环境开发、测试、生产切换。日志与监控Agent的决策过程特别是verbose输出是宝贵的调试信息。将其结构化的日志如JSON格式输出到文件或日志系统。监控工具调用成功率、延迟和成本尤其是LLM的token消耗。测试单元测试单独测试每个Skill函数模拟各种正常和异常输入。集成测试测试整个Agent流程模拟用户输入验证Agent是否能正确选择并调用工具。模拟Mocking在测试中模拟第三方API调用和LLM响应使测试快速、稳定且不产生费用。7.2 进阶能力扩展你的智能体可以从这里走向更强大的应用集成更多技能数据库操作创建查询、更新数据库的Skill让Agent成为数据助手。文件操作读写本地文件、解析PDF/Word。外部系统API连接你的CRM、ERP、工单系统。代码执行注意安全沙箱环境是必须的让Agent可以执行简单的Python代码片段来分析数据或进行计算。实现智能规划Planning 当前的Agent是“反应式”的一步一想。更高级的Agent可以提前规划多步任务。可以探索LangChain的Plan-and-Execute代理架构。微软Semantic Kernel的Planner。让LLM生成一个任务执行计划如JSON列表然后逐步执行。增加检索增强生成RAG能力 让Agent能够访问你私有的知识库文档、手册、代码库。结合LlamaIndex或LangChain的RetrievalQA打造专属知识助手。构建多智能体Multi-Agent系统 创建多个具有不同专长的Agent如“数据分析师”、“前端工程师”、“测试员”让它们通过协作完成复杂项目。AutoGen框架是研究此方向的绝佳选择。添加持久化记忆 将对话历史、学到的用户偏好存入数据库如SQLite、PostgreSQL实现跨会话的记忆。提供Web界面或API 使用FastAPI、Flask或Gradio为你的Agent构建一个Web界面或REST API使其能被其他系统调用。从理解Agent与Skill的基本概念到搭建开发环境再到亲手创建两个自定义Skill并构建出能自主决策的智能体我们完成了一个完整的入门到实践的闭环。记住Agent开发的核心在于将复杂问题分解为LLM的“思考”和可执行“工具”的有机结合。