用Python的ReAct机制,让LLM主动调用本地API关灯、发邮件
用Python的ReAct机制让LLM主动调用本地API关灯、发邮件想象这样一个场景你对电脑说“我准备睡了关灯并把明天上午的会议提醒发到我邮箱”然后灯真的灭了邮件真的发出去了。这不是科幻电影而是ReAct机制赋予大语言模型LLM的“动手能力”。传统LLM只能被动生成文本像一位只动口不动手的顾问。而ReActReasoning Acting让LLM能主动推理并调用外部工具真正改变物理世界或数字世界的状态。本文将带你用Python从零搭建一个ReAct智能体让它能调用本地API关灯模拟硬件和发邮件SMTP全程代码可运行、可扩展。一、ReAct是什么为什么它能让LLM“动手”ReAct是2022年提出的一种LLM推理与行动协同范式。核心思想是让模型在生成最终答案前先输出“思考Thought”和“行动Action”的中间步骤并根据行动结果Observation调整后续推理。传统Chain-of-ThoughtCoT只有推理链没有外部交互传统工具调用Toolformer只有函数调用缺乏动态推理。ReAct把两者结合形成“思考→行动→观察→再思考”的闭环。例如用户说“关灯并发邮件”ReAct智能体可能这样思考Thought 1: 用户要求两个动作先关灯再发邮件。Action 1:turn_off_lamp()Observation 1:Lamp is offThought 2: 关灯成功现在发邮件。Action 2:send_email(recipientmeexample.com, subject明天会议, body上午10点)Observation 2:Email sentThought 3: 所有任务完成回复用户。这种显式的推理轨迹不仅可解释还能在行动失败时自我修正如重试或换方案。二、整体架构设计我们的系统由四部分组成LLM引擎选用OpenAI的GPT-3.5/4 API也可替换为本地开源模型如Qwen。工具函数Python函数执行具体操作关灯、发邮件模拟或真实调用本地API。ReAct控制器核心循环维护对话历史解析LLM输出的Thought/Action/Observation并执行工具。用户接口命令行输入问题输出最终结果。为了安全所有本地API调用都限制在用户明确授权的动作范围内且邮件发送使用测试环境配置。三、准备工作安装依赖与配置pipinstallopenai python-dotenv创建.env文件存放OpenAI密钥OPENAI_API_KEYsk-xxxxxxxxxxxxxxxxxxxxxxxx新建config.pyimportosfromdotenvimportload_dotenv load_dotenv()OPENAI_API_KEYos.getenv(OPENAI_API_KEY)四、定义工具函数关灯与发邮件为了演示我们模拟关灯操作打印状态并用SMTP发送真实邮件建议使用QQ/163邮箱的授权码。tools.pyimportsmtplibfromemail.mime.textimportMIMETextfromemail.headerimportHeader# 模拟灯的状态lamp_status{on:True}defturn_off_lamp()-str:关闭本地智能灯模拟globallamp_statusiflamp_status[on]:lamp_status[on]FalsereturnLamp is now OFFreturnLamp is already OFFdefturn_on_lamp()-str:开启本地智能灯模拟globallamp_statusifnotlamp_status[on]:lamp_status[on]TruereturnLamp is now ONreturnLamp is already ONdefsend_email(recipient:str,subject:str,body:str)-str: 通过SMTP发送邮件请替换为你的邮箱配置 # 配置你的邮箱信息建议用环境变量smtp_serversmtp.qq.com# 以QQ邮箱为例smtp_port465# SSL端口sender_emailyour_emailqq.compasswordyour_authorization_code# 授权码不是QQ密码try:msgMIMEText(body,plain,utf-8)msg[Subject]Header(subject,utf-8)msg[From]sender_email msg[To]recipientwithsmtplib.SMTP_SSL(smtp_server,smtp_port)asserver:server.login(sender_email,password)server.sendmail(sender_email,[recipient],msg.as_string())returnfEmail sent to{recipient}successfullyexceptExceptionase:returnfFailed to send email:{str(e)}为了安全实际生产环境应限制recipient为白名单且不要在代码中硬编码密码改用环境变量。五、工具描述与注册LLM需要知道有哪些工具可用、每个工具的参数和用途。我们构建一个工具注册表供ReAct控制器查询。tool_registry.pyfromtoolsimportturn_off_lamp,turn_on_lamp,send_email TOOLS[{name:turn_off_lamp,func:turn_off_lamp,description:关闭智能灯。无参数。,parameters:{}},{name:turn_on_lamp,func:turn_on_lamp,description:开启智能灯。无参数。,parameters:{}},{name:send_email,func:send_email,description:发送邮件。需要提供收件人、主题和正文。,parameters:{recipient:string, 收件人邮箱地址,subject:string, 邮件主题,body:string, 邮件正文}}]defget_tool_description():生成供LLM理解的工具列表描述desc你可以使用以下工具函数每次只调用一个工具。\nfortinTOOLS:descf-{t[name]}:{t[description]}\nift[parameters]:descf 参数:{t[parameters]}\nreturndescdefexecute_tool(name:str,**kwargs):根据工具名和参数执行对应函数fortinTOOLS:ift[name]name:returnt[func](**kwargs)returnfError: Tool{name}not found六、构建ReAct提示模板ReAct的关键在于提示工程。我们需要指导LLM按固定格式输出Thought、Action、Action Input并在最后输出Final Answer。prompt_builder.pyfromtool_registryimportget_tool_description SYSTEM_PROMPT你是一个能调用工具来完成任务的人工智能助手。你的回答必须遵循以下格式 Thought: 你对于当前情况的思考。 Action: 要调用的工具名称必须是以下列表中的之一。 Action Input: 工具的输入参数以JSON格式提供如果没有参数则写{{}}。 在收到工具的观察结果Observation后你可以继续思考并调用下一个工具或者输出最终答案。 当你已经获得足够信息并可以回答用户时使用以下格式 Thought: 我现在知道最终答案了。 Final Answer: 对用户的最终回复。 工具列表 {tool_description} 请严格遵守格式不要输出多余内容。 defbuild_initial_prompt(user_query:str)-list:tool_descget_tool_description()system_msgSYSTEM_PROMPT.format(tool_descriptiontool_desc)return[{role:system,content:system_msg},{role:user,content:user_query}]七、解析LLM输出LLM可能返回多行文本我们需要从中提取Thought、Action、Action Input或Final Answer。这里用正则表达式解析。parser.pyimportreimportjsondefparse_llm_output(text:str): 解析LLM的输出返回类型和内容 返回: (thought, content) 或 (action, name, kwargs) 或 (final, answer) linestext.strip().split(\n)thoughtNoneactionNoneaction_inputNonefinal_answerNoneforlineinlines:ifline.startswith(Thought:):thoughtline[len(Thought:):].strip()elifline.startswith(Action:):actionline[len(Action:):].strip()elifline.startswith(Action Input:):rawline[len(Action Input:):].strip()# 尝试解析JSONtry:action_inputjson.loads(raw)except:# 如果解析失败当作空字典或简单字符串处理ifraw{}orraw:action_input{}else:# 尝试提取键值对非严格JSONaction_input{input:raw}elifline.startswith(Final Answer:):final_answerline[len(Final Answer:):].strip()iffinal_answerisnotNone:return(final,final_answer)elifactionisnotNone:return(action,action,action_inputor{})else:# 如果没有明确Action但有Thought可能模型需要重新提示return(thought,thoughtortext)实际中LLM可能将Action Input写成{recipient:mexx.com,subject:提醒,body:开会}我们需要兼容。八、ReAct主循环推理-行动-观察控制器维护消息历史每次循环将用户输入和工具执行结果拼入对话直到LLM输出Final Answer或达到最大步数。react_loop.pyimportopenaifromconfigimportOPENAI_API_KEYfromprompt_builderimportbuild_initial_promptfromparserimportparse_llm_outputfromtool_registryimportexecute_tool openai.api_keyOPENAI_API_KEYdefrun_react_loop(user_query:str,max_steps5):messagesbuild_initial_prompt(user_query)step0final_answerNonewhilestepmax_steps:print(f\n--- Step{step1}---)# 调用LLMresponseopenai.ChatCompletion.create(modelgpt-3.5-turbo,messagesmessages,temperature0.2,# 低温度保证格式稳定)llm_outputresponse.choices[0].message.contentprint(LLM Output:\n,llm_output)# 解析result_typeparse_llm_output(llm_output)ifresult_type[0]final:final_answerresult_type[1]breakelifresult_type[0]action:_,action_name,action_kwargsresult_typeprint(fCalling tool:{action_name}with{action_kwargs})# 执行工具observationexecute_tool(action_name,**action_kwargs)print(Observation:,observation)# 将LLM输出和观察结果加入历史messages.append({role:assistant,content:llm_output})messages.append({role:user,content:fObservation:{observation}})else:# 如果解析为thought但没有action强制要求模型给出actionmessages.append({role:assistant,content:llm_output})messages.append({role:user,content:请提供 Action 和 Action Input 来继续。})step1iffinal_answerisNone:final_answer抱歉我在限定步骤内无法完成任务。returnfinal_answer九、组合运行关灯并发邮件现在把所有模块拼起来写一个main.pyfromreact_loopimportrun_react_loopif__name____main__:user_inputinput(请输入您的指令: )resultrun_react_loop(user_input)print(\n 最终答案 )print(result)测试一次对话用户输入关灯然后给我自己的邮箱发一封邮件主题是“晚安”内容是“今天辛苦了明天上午10点开会”。运行日志简化--- Step 1 --- LLM Output: Thought: 用户要求关灯并发送邮件。先关灯。 Action: turn_off_lamp Action Input: {} Calling tool: turn_off_lamp with {} Observation: Lamp is now OFF --- Step 2 --- LLM Output: Thought: 关灯成功。现在发送邮件。收件人是用户自己的邮箱主题“晚安”内容“今天辛苦了明天上午10点开会”。 Action: send_email Action Input: {recipient:your_emailqq.com,subject:晚安,body:今天辛苦了明天上午10点开会。} Calling tool: send_email with {recipient: your_emailqq.com, subject: 晚安, body: 今天辛苦了明天上午10点开会。} Observation: Email sent to your_emailqq.com successfully --- Step 3 --- LLM Output: Thought: 我现在知道最终答案了。 Final Answer: 已完成关灯并发送邮件到您的邮箱请查收。 最终答案 已完成关灯并发送邮件到您的邮箱请查收。十、进阶本地API真实调用上述turn_off_lamp是模拟。若要真实控制智能灯如Philips Hue或小米只需替换函数实现通过HTTP请求调用本地网关API。例如使用requests库importrequestsdefturn_off_lamp_real():urlhttp://192.168.1.100/api/your_token/lights/1/statepayload{on:False}resprequests.put(url,jsonpayload)ifresp.status_code200:returnLamp turned off via APIelse:returnfAPI error:{resp.text}同理邮件部分可用公司内部的邮件中继API。十一、安全性考量ReAct智能体调用本地API时必须防范指令注入和越权操作。建议参数白名单send_email的recipient限制为预先授权的邮箱列表。工具沙箱对执行环境进行隔离避免eval或exec危险函数。人工确认对关键操作如发邮件、开锁增加二次确认机制。日志审计记录所有Thought/Action/Observation便于追溯。十二、扩展支持多工具并行与流式输出当前每次只调用一个工具效率较低。可改进为在Action行支持多个工具调用如Action: turn_off_lamp, send_email。使用异步执行asyncio并行调用无依赖的工具。使用流式APIstreamTrue实时输出思考过程提升体验。十三、总结本文用200多行Python代码实现了完整的ReAct智能体让它能主动调用本地API关灯和发邮件。你学到的不仅是代码更是一种范式迁移LLM从文本生成器进化为任务执行者。ReAct的核心价值在于可解释性每一步思考都透明可见。容错性观察结果可反馈到下一步推理。可扩展性添加新工具只需注册函数和描述。未来你可以将ReAct连接到日历、数据库、物联网中枢甚至通过RPA控制企业软件。当LLM真正“动手”时自动化的边界将被重新定义。现在打开你的终端跑通这段代码对你的电脑说一句“关灯”——物理世界会给出回应。推荐阅读我的电子文档/书籍管理

相关新闻

Claude Code与Codex六大组件拆解,Coding Agent怎么把任务做稳

Claude Code与Codex六大组件拆解,Coding Agent怎么把任务做稳

周五傍晚的研发工位上,CI流水线突然飘红,摆在面前的需求简单直白,仅仅是修复会员跨月折扣的失败测试。交给编程智能体处理,它很快定位到日期边界的逻辑漏洞,修改代码之后,目标用例顺利由红转绿,…

2026/10/20 23:30:26 阅读更多 →
《守望先锋》温斯顿生存指南:从暴毙复盘到团队协同

《守望先锋》温斯顿生存指南:从暴毙复盘到团队协同

这次我们来看一个《守望先锋》游戏中的典型场景分析:猩猩(温斯顿)玩家跳入敌阵瞬间被秒,然后回头责怪辅助不奶。这种情况在游戏中非常常见,但问题真的全在辅助身上吗?这篇文章将深入拆解这个场景&#xff0…

2026/10/21 2:50:13 阅读更多 →
Unity WebSocket客户端实现:从RFC 6455协议到NativeWebSocket源码解析

Unity WebSocket客户端实现:从RFC 6455协议到NativeWebSocket源码解析

1. 项目概述:为什么Unity需要一个纯粹的WebSocket客户端? 在Unity里做网络通信,你肯定用过UnityWebRequest或者.Net自带的HttpClient,但一提到WebSocket,很多人就有点犯怵。Unity官方没有提供原生的WebSocket客户端&am…

2026/10/20 15:42:00 阅读更多 →

最新新闻

spooling假脱机模拟:2请求进程+1输出进程的并发控制与信号量实现

spooling假脱机模拟:2请求进程+1输出进程的并发控制与信号量实现

简介:这份资源围绕操作系统中的Spooling假脱机输入输出模拟技术展开,面向计算机专业学生及系统设计学习者,帮助理解如何通过磁盘缓冲将慢速I/O设备虚拟化,实现多进程共享打印机等外设。包内共11个文件,以5个class编译文…

2026/10/21 2:52:02 阅读更多 →
原生应用接入AI的伦理与合规:从开源许可到隐私边界

原生应用接入AI的伦理与合规:从开源许可到隐私边界

1. 为什么原生应用开发者绕不开“AI伦理”这道门槛“AI伦理”这四个字放在前几年,很多做原生应用开发的同行会觉得这是大厂法务和研究院才需要操心的事。但这两年情况完全不一样了——几乎每个原生应用都在往里面塞AI能力,从刷脸登录、智能客服&#xff…

2026/10/21 2:52:02 阅读更多 →
PowerBuilder 11.5 环境搭建与数据窗口连库实战指南

PowerBuilder 11.5 环境搭建与数据窗口连库实战指南

简介:PowerBuilder 11.5 安装文件资源包,面向需要搭建经典 PB 开发环境的学习者与维护老项目的开发者。PowerBuilder 是 Sybase 推出的快速应用开发工具,11.5 版本在数据窗口、数据库连接与客户端应用开发方面较为成熟,适合用于课…

2026/10/21 2:52:02 阅读更多 →
JSP+Servlet学生管理系统毕设实战指南

JSP+Servlet学生管理系统毕设实战指南

简介:这是一份面向Java Web初学者与高校课程实践者的完整学生信息管理系统教学资源,基于JSP与Servlet技术栈实现MVC分层架构,覆盖动态网页开发、HTTP请求处理、JDBC数据库操作及Tomcat容器部署等核心知识点。资源包共576个文件,包…

2026/10/21 2:52:02 阅读更多 →
MPlayer启用VAAPI硬解:编译、配置与验证实战

MPlayer启用VAAPI硬解:编译、配置与验证实战

手里还有一台老机器,跑着轻量级Linux桌面,平时看本地视频一直用MPlayer的-vo和-slave接口,自动化脚本都绑死在上面。后来换了4K片源,CPU直接拉满,画面一卡一卡的,我才认真考虑给MPlayer接上VAAPI硬解。折腾…

2026/10/21 2:52:02 阅读更多 →
知网AIGC检测原理与降AI工具实测:从标红到通过的完整攻略

知网AIGC检测原理与降AI工具实测:从标红到通过的完整攻略

去年有一阵子,实验室里的几位同学轮流来找我,说同一个问题:初稿明明是自己熬夜搭出来的框架,再用AI润了一轮,结果往知网AIGC检测系统一放,直接标红一片,相似度干到百分之四五十,要求…

2026/10/21 2:51:01 阅读更多 →

日新闻

统一内存+量化:锐龙AI Max PRO 400如何本地跑320B大模型

统一内存+量化:锐龙AI Max PRO 400如何本地跑320B大模型

这段时间硬件圈和AI圈都在传一个事:AMD锐龙AI Max PRO 400系列把最高192GB统一内存搬进了PC形态的产品里,按宣传口径,本地跑320B参数的大家伙不再是纸面概念。我第一反应倒不是“内存真大”,而是这话背后的含金量——它把过去只能…

2026/10/21 0:00:11 阅读更多 →
AI助手深度使用体验:对话质量、长文本处理与指令遵循如何留住用户

AI助手深度使用体验:对话质量、长文本处理与指令遵循如何留住用户

1. 从“封号重开”这个行为说起:用户到底在追求什么“封号重开”这四个字,放在任何产品语境里都带着一股决绝的味道。一个账号从零开始积累使用记录、调教偏好、沉淀工作流,背后是大量的时间和精力成本。正常情况下,没有人愿意主动…

2026/10/21 0:00:11 阅读更多 →
ROS中YOLOv3工程落地:通信同步、坐标变换与性能调优

ROS中YOLOv3工程落地:通信同步、坐标变换与性能调优

1. 为什么在ROS里硬刚YOLOv3不是“炫技”,而是工程落地的必经之路我第一次在某高校机器人实验室看到那台搭载Jetson TX2的移动底盘原地打转、反复识别错垃圾桶时,就意识到:单纯跑通darknet_ros demo和真正让YOLOv3在ROS里“干活”&#xff0c…

2026/10/21 0:00:11 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/20 10:02:13 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/20 20:43:59 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/20 2:50:50 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 10:02:08 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 9:50:17 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 9:50:45 阅读更多 →