手把手教你从0到1搭建一个AI Agent(智能体)

手把手教你从0到1搭建一个AI Agent(智能体)

LLM为什么需要agent化

虽然大语言模型的能力很强大,但是Llm仅限于用于训练的知识,这些知识很快会过时,所以llm有以下缺点

  • 幻觉
  • 结果并不总是真实的
  • 对时事的了解有限或一无所知
  • 难以应对复杂推理和计算

例如:买高铁票

在这里插入图片描述

(虽然LLM完全理解了买票的行为,但是它本身并不知道“我”所处的城市,列车的时刻表,价格等等信息)
而基于大模型的Agent (LLM based Agent) 可以利用外部工具来克服以上缺点。

打个招聘广告

作者目前的公司正在热招,后端(包括大模型)、前端、Android,产品、运营都有岗!
📍Base:上海/成都
💰薪资:绝对香(BAT对标)
内推不卡简历,直达Team Leader!

岗位和投递链接: https://construct.jobs.feishu.cn/s/3aAep9X0K7I

ReAct Agent

ReAct Agent 论文

LLM Agent 的升级之路:

Standard IO(直接回答) -> COT(chain-of-thought)(思维链) -> Action-Only (Function calling) -> Reason + Action
ReAct = Reasoning(推理) + Action(行动)

在这里插入图片描述

ReAct Agent 的组成部分 (通过LangChain实现)

  • Models:LLM
  • Prompts:对Agent的指令、约束
  • Memory : 记录Action执行状态 & 缓存已知信息
  • Indexes : 用于结构化文档,以便和模型交互
  • Chains :Langchain的核心(链)
  • Agent

ReAct Agent 的prompt 模板

from langchain_core.prompts import PromptTemplate template ='''Answer the following questions as best you can. You have access to the following tools: {tools} Use the following format: Question: the input question you must answer Thought: you should always think about what to do Action: the action to take, should be one of [{tool_names}] Action Input: the input to the action Observation: the result of the action ... (this Thought/Action/Action Input/Observation can repeat N times) Thought: I now know the final answer Final Answer: the final answer to the original input question Begin! Question: {input} Thought:{agent_scratchpad}''' prompt = PromptTemplate.from_template(template)

代码

手写一个能帮忙买火车票的智能Agent
注:火车票相关API均为mock

安装 & import依赖

pip install langchain pip install uuid pip install pydantic import json import sys from typing import List, Optional, Dict, Any, Tuple, Union from uuid import UUID from langchain.memory import ConversationTokenBufferMemory from langchain.tools.render import render_text_description from langchain_core.callbacks import BaseCallbackHandler from langchain_core.language_models import BaseChatModel from langchain_core.output_parsers import PydanticOutputParser, StrOutputParser from langchain_core.outputs import GenerationChunk, ChatGenerationChunk, LLMResult from langchain_core.prompts import PromptTemplate from langchain_core.tools import StructuredTool from langchain_openai import ChatOpenAI from pydantic import BaseModel, Field, ValidationError 

定义工具(Tools)

细节可参考LangChain定义Tool

from typing import List from langchain_core.tools import StructuredTool defsearch_train_ticket( origin:str, destination:str, date:str, departure_time_start:str, departure_time_end:str)-> List[dict[str,str]]:"""按指定条件查询火车票"""# mock train listreturn[{"train_number":"G1234","origin":"北京","destination":"上海","departure_time":"2024-06-01 8:00","arrival_time":"2024-06-01 12:00","price":"100.00","seat_type":"商务座",},{"train_number":"G5678","origin":"北京","destination":"上海","departure_time":"2024-06-01 18:30","arrival_time":"2024-06-01 22:30","price":"100.00","seat_type":"商务座",},{"train_number":"G9012","origin":"北京","destination":"上海","departure_time":"2024-06-01 19:00","arrival_time":"2024-06-01 23:00","price":"100.00","seat_type":"商务座",}]defpurchase_train_ticket( train_number:str,)->dict:"""购买火车票"""return{"result":"success","message":"购买成功","data":{"train_number":"G1234","seat_type":"商务座","seat_number":"7-17A"}} search_train_ticket_tool = StructuredTool.from_function( func=search_train_ticket, name="查询火车票", description="查询指定日期可用的火车票。",) purchase_train_ticket_tool = StructuredTool.from_function( func=purchase_train_ticket, name="购买火车票", description="购买火车票。会返回购买结果(result), 和座位号(seat_number)",) finish_placeholder = StructuredTool.from_function( func=lambda:None, name="FINISH", description="用于表示任务完成的占位符工具") tools =[search_train_ticket_tool, purchase_train_ticket_tool, finish_placeholder]

Prompt

主要任务Prompt

prompt_text =""" 你是强大的AI火车票助手,可以使用工具与指令查询并购买火车票 你的任务是: {task_description} 你可以使用以下工具或指令,它们又称为动作或actions: {tools} 当前的任务执行记录: {memory} 按照以下格式输出: 任务:你收到的需要执行的任务 思考: 观察你的任务和执行记录,并思考你下一步应该采取的行动 然后,根据以下格式说明,输出你选择执行的动作/工具: {format_instructions} """

最终回复Prompt

final_prompt =""" 你的任务是: {task_description} 以下是你的思考过程和使用工具与外部资源交互的结果。 {memory} 你已经完成任务。 现在请根据上述结果简要总结出你的最终答案。 直接给出答案。不用再解释或分析你的思考过程。 """

一些方便编程的工具类

classAction(BaseModel):"""结构化定义工具的属性""" name:str= Field(description="工具或指令名称") args: Optional[Dict[str, Any]]= Field(description="工具或指令参数,由参数名称和参数值组成")classMyPrintHandler(BaseCallbackHandler):"""自定义LLM CallbackHandler,用于打印大模型返回的思考过程"""def__init__(self): BaseCallbackHandler.__init__(self)defon_llm_new_token( self, token:str,*, chunk: Optional[Union[GenerationChunk, ChatGenerationChunk]]=None, run_id: UUID, parent_run_id: Optional[UUID]=None,**kwargs: Any,)-> Any: end ="" content = token + end sys.stdout.write(content) sys.stdout.flush()return token defon_llm_end(self, response: LLMResult,**kwargs: Any)-> Any: end ="" content ="\n"+ end sys.stdout.write(content) sys.stdout.flush()return response 

定义Agent

classMyAgent:def__init__( self, llm: BaseChatModel = ChatOpenAI( model="gpt-4-turbo",# agent用GPT4效果好一些,推理能力较强 temperature=0, model_kwargs={"seed":42},), tools=None, prompt:str="", final_prompt:str="", max_thought_steps: Optional[int]=10,):if tools isNone: tools =[] self.llm = llm self.tools = tools self.final_prompt = PromptTemplate.from_template(final_prompt) self.max_thought_steps = max_thought_steps # 最多思考步数,避免死循环 self.output_parser = PydanticOutputParser(pydantic_object=Action) self.prompt = self.__init_prompt(prompt) self.llm_chain = self.prompt | self.llm | StrOutputParser()# 主流程的LCEL self.verbose_printer = MyPrintHandler()def__init_prompt(self, prompt):return PromptTemplate.from_template(prompt).partial( tools=render_text_description(self.tools), format_instructions=self.__chinese_friendly( self.output_parser.get_format_instructions(),))defrun(self, task_description):"""Agent主流程"""# 思考步数 thought_step_count =0# 初始化记忆 agent_memory = ConversationTokenBufferMemory( llm=self.llm, max_token_limit=4000,) agent_memory.save_context({"input":"\ninit"},{"output":"\n开始"})# 开始逐步思考while thought_step_count < self.max_thought_steps:print(f">>>>Round: {thought_step_count}<<<<") action, response = self.__step( task_description=task_description, memory=agent_memory )# 如果是结束指令,执行最后一步if action.name =="FINISH":break# 执行动作 observation = self.__exec_action(action)print(f"----\nObservation:\n{observation}")# 更新记忆 self.__update_memory(agent_memory, response, observation) thought_step_count +=1if thought_step_count >= self.max_thought_steps:# 如果思考步数达到上限,返回错误信息 reply ="抱歉,我没能完成您的任务。"else:# 否则,执行最后一步 final_chain = self.final_prompt | self.llm | StrOutputParser() reply = final_chain.invoke({"task_description": task_description,"memory": agent_memory })return reply def__step(self, task_description, memory)-> Tuple[Action,str]:"""执行一步思考""" response =""for s in self.llm_chain.stream({"task_description": task_description,"memory": memory }, config={"callbacks":[ self.verbose_printer ]}): response += s action = self.output_parser.parse(response)return action, response def__exec_action(self, action: Action)->str: observation ="没有找到工具"for tool in self.tools:if tool.name == action.name:try:# 执行工具 observation = tool.run(action.args)except ValidationError as e:# 工具的入参异常 observation =(f"Validation Error in args: {str(e)}, args: {action.args}")except Exception as e:# 工具执行异常 observation =f"Error: {str(e)}, {type(e).__name__}, args: {action.args}"return observation @staticmethoddef__update_memory(agent_memory, response, observation): agent_memory.save_context({"input": response},{"output":"\n返回结果:\n"+str(observation)})@staticmethoddef__chinese_friendly(string)->str: lines = string.split('\n')for i, line inenumerate(lines):if line.startswith('{')and line.endswith('}'):try: lines[i]= json.dumps(json.loads(line), ensure_ascii=False)except:passreturn'\n'.join(lines)

测试

if __name__ =="__main__": my_agent = MyAgent( tools=tools, prompt=prompt_text, final_prompt=final_prompt,) task ="帮我买24年6月1日早上去上海的火车票" reply = my_agent.run(task)print(reply)

结果

第一轮思考

Agent根据要求,选择了需要使用的Tool,组装了请求参数并完成了调用。
(还可以多定义一些Tools,比如获取当前位置的,获取今天日期的工具等等,这样这里的查询火车票的参数可以更智能)

>>>>Round: 0<<<< 任务:帮我买24年6月1日早上去上海的火车票 思考: 根据任务需求,首先需要查询2024年6月1日早上从当前位置到上海的火车票。这需要使用“查询火车票”工具,指定出发地、目的地、日期以及早上的时间范围。 动作/工具: {"name":"查询火车票", "args":{"origin":"当前位置", "destination":"上海", "date":"2024-06-01", "departure_time_start":"00:00", "departure_time_end":"12:00"}} ---- Observation: [{'train_number':'G1234', 'origin':'北京', 'destination':'上海', 'departure_time':'2024-06-01 8:00', 'arrival_time':'2024-06-01 12:00', 'price':'100.00', 'seat_type':'商务座'}, {'train_number':'G5678', 'origin':'北京', 'destination':'上海', 'departure_time':'2024-06-01 18:30', 'arrival_time':'2024-06-01 22:30', 'price':'100.00', 'seat_type':'商务座'}, {'train_number':'G9012', 'origin':'北京', 'destination':'上海', 'departure_time':'2024-06-01 19:00', 'arrival_time':'2024-06-01 23:00', 'price':'100.00', 'seat_type':'商务座'}]

第二轮思考

根据查询出的车票信息去调用购票的Tool

>>>>Round: 1<<<< 任务:帮我买24年6月1日早上去上海的火车票 思考: 根据查询结果,有三个车次可供选择,但只有车次G1234符合早上出发的要求,因此应选择购买这个车次的票。 动作/工具: {"name":"购买火车票", "args":{"train_number":"G1234"}} ---- Observation: {'result':'success', 'message':'购买成功', 'data':{'train_number':'G1234', 'seat_type':'商务座', 'seat_number':'7-17A'}}

第三轮思考

LLM识别到任务已完成,输出了结果

>>>>Round: 2<<<< 任务:帮我买24年6月1日早上去上海的火车票 思考: 根据执行记录,已经成功购买了2024年6月1日早上从北京到上海的火车票(车次G1234)。因此,接下来的任务是完成这个购票任务。 动作/工具: {"name":"FINISH"} 购买成功。您已成功购买2024年6月1日早上从北京出发前往上海的火车票,车次为G1234,座位类型为商务座,座位号为7-17A。 

Read more

搭建一个基于Django框架的WebApi项目

搭建一个基于Django框架的WebApi项目

让我们一起走向未来 🎓作者简介:全栈领域优质创作者 🌐个人主页:百锦再@新空间代码工作室 📞工作室:新空间代码工作室(提供各种软件服务) 💌个人邮箱:[[email protected]] 📱个人微信:15045666310 🌐网站:https://meihua150.cn/ 💡座右铭:坚持自己的坚持,不要迷失自己!要快乐 目录 * 让我们一起走向未来 * 一、创建Django项目 * 二、安装相关依赖 * 三、配置MySQL数据库 * 四、配置Redis缓存 * 五、配置JWT中间件 * 六、配置Swagger接口文档 * 七、创建示例API * 八、总结 一、创建Django项目 首先,确保你的环境中已安装Django。如果没有,可以通过以下命令安装: pip install django

MCP 教程:将 Figma 设计稿转化为前端代码

📋 MCP:将 Figma 设计稿转化为前端代码 🎯 概述 还在手动从设计稿提取样式、编写基础代码?试试 Trae IDE 的模型上下文协议(MCP)功能吧。通过使用 MCP Server - Figma AI Bridge,自动将你的 Figma 设计稿转换为整洁的前端代码,并生成相应的网页。简单高效,无需复杂配置,跟随文中的步骤操作,即可体验智能化的设计交付。让我们开始吧! 🚀 效果展示 使用 Trae IDE 的 Figma AI Bridge MCP Server 将设计稿转换为前端代码的效果展示: * 设计稿到代码的自动转换: 无需手动编写 HTML、CSS 代码 * 响应式布局: 自动生成适配不同屏幕尺寸的响应式代码 * 组件化结构: 智能识别设计中的组件,生成可复用的组件代码

【魅影AI远程控制】:基于WebRTC+AI的智能远程控制解决方案

【魅影AI远程控制】:基于WebRTC+AI的智能远程控制解决方案

x 魅影AI远程控制:基于WebRTC+AI的智能远程控制解决方案 📖 前言 在移动互联网时代,远程控制技术已经成为企业IT支持、无障碍辅助、智能家居等领域的重要工具。传统的远程控制方案往往需要用户手动操作,效率低下。今天,我将为大家介绍一款革命性的魅影AI远程控制应用,它通过WebRTC实时通信和AI智能识别技术,实现了"语音指令→智能理解→自动执行"的完整闭环,让远程控制变得像对话一样简单。 🎯 项目概述 什么是魅影AI远程控制? 魅影AI远程控制是一款双端合一的Android应用,集成了远程控制、AI语音助手、OCR识别等多项前沿技术。它最大的特点是智能化:用户只需说出需求,AI就能理解意图并自动完成操作。 核心价值 传统远控:手动点击、滑动 → 繁琐、低效 AI远控:语音命令、智能识别、自动执行 → 简单、高效、智能 一句话描述: "说出你的需求,AI帮你完成" -

【通过 Vue 实例劫持突破 Web 编辑器的粘贴限制】

【通过 Vue 实例劫持突破 Web 编辑器的粘贴限制】

逆向实战:通过 Vue 实例劫持突破 Web 编辑器的粘贴限制 * 一、AI实践代码编辑器:Vue 实例劫持方案(含分析,可直接跳过至4.1查看方法) * 1. 现象与初探:被禁用的 Ctrl+V * 技术视角的初步审视 * 逆向的逻辑前提 * 2. 逆向分析:寻找逻辑的“命门” * 突破口:利用 I18N 国际化配置追踪 * 核心文件追踪:锁定 `answer-code-editor.js` * 代码逻辑解剖:拦截机制的实现 * 3. 攻克方案:Vue 实例的运行时劫持 * 第一步:获取 Vue 实例的“后门” * 第二步:函数劫持(Monkey Patch) * 第三步:状态机的一致性重构 * 第四步: