74-AI私人助理实战-天气邮件新闻日程-多工具Agent-Streamlit界面
文章目录
【74.Python+AI】实战:AI私人助理——查天气、写邮件、搜新闻、管日程的全能Agent
📖 文章简介: 本文是Agent板块的收官实战,把前面学过的ReAct循环、工具定义、多轮任务规划全部串起来,从零搭建一个AI私人助理。文章实现5个工具的集成(天气查询、新闻搜索、邮件起草、日程读写、通用计算),支持"明天下雨就提醒我带伞,并把下午的会改到线上"这类跨工具的多轮任务规划,最后用Streamlit搭建可视化聊天界面实现一键运行。配有Mermaid架构图展示任务规划与工具串联的完整数据流,适合完整学完Agent板块、需要一个综合项目检验学习成果的开发者。

🎬 个人主页: 源码骑士
❄ 专栏传送门: 《Android开发基础》《python基础课程》
⭐️热衷从源码视角拆解技术底层原理,将复杂架构讲得通俗易懂
🎬 源码骑士的简介:
5年Android Framework系统开发经验,曾主导多项系统级性能优化专项
技术栈覆盖Android系统全链路(Binder/Handler/AMS/WMS/启动流程)及Java后端全家桶(Spring + MyBatis + Redis + Oracle)
累计产出原创技术文章100+篇,文章以流程图为特色,被读者评价为"看一篇胜过啃一周源码"
导入语
前面几篇文章,我们把Agent的零件一个个造了出来:ReAct循环、工具定义、安全控制。零散的知识点学完了,是时候把它们组装成一台真正能跑的机器。
这篇文章的目标:一个能理解复合指令的私人助理。 你说"帮我看看明天北京天气,如果下雨就把明天的户外跑步改成室内,再写封邮件告诉教练",它能自己拆解成三步、串起三个工具、给你一份完整的执行结果。
1 ~> 整体架构
1.1 项目结构
assistant/
├─ tools.py # 5个工具的实现
├─ agent.py # ReAct Agent核心
├─ app.py # Streamlit界面
└─ requirements.txt
2 ~> 五个工具的实现
2.1 tools.py
import requests, json, datetime
# ---------- 工具1:天气查询 ----------
def get_weather(city: str) -> str:
"""查询城市天气(示例用wttr.in免费接口)"""
try:
r = requests.get(f"https://wttr.in/{city}?format=j1", timeout=10)
data = r.json()
today = data["weather"][0]
desc = today["hourly"][4]["lang_zh"][0]["value"]
temp = today["avgtempC"]
rain = today["hourly"][4]["chanceofrain"]
return f"{city}明天天气:{desc},平均气温{temp}℃,降雨概率{rain}%"
except Exception as e:
return f"天气查询失败:{e}"
# ---------- 工具2:新闻搜索 ----------
def search_news(keyword: str) -> str:
"""搜索最新新闻(示例用模拟数据,可替换为Tavily/NewsAPI)"""
# 实际接入时替换为真实API
return f"关于'{keyword}'的最新新闻:\n1. [示例]相关新闻标题一\n2. [示例]相关新闻标题二"
# ---------- 工具3:邮件起草 ----------
def draft_email(recipient: str, subject: str, body_hint: str) -> str:
"""起草邮件(生成草稿,不直接发送——安全第一)"""
return (f"【邮件草稿】\n收件人:{recipient}\n主题:{subject}\n"
f"正文草稿:根据'{body_hint}'生成的邮件正文...\n"
f"[草稿待人工确认后发送]")
# ---------- 工具4:日程管理 ----------
_schedule = {} # 演示用内存存储,生产换数据库
def manage_schedule(action: str, date: str, event: str = "") -> str:
"""日程增查改。action: add/list/update"""
if action == "add":
_schedule.setdefault(date, []).append(event)
return f"已添加日程:{date} - {event}"
elif action == "list":
events = _schedule.get(date, [])
return f"{date}的日程:{events if events else '无安排'}"
elif action == "update":
return f"{date}的日程已更新为:{event}"
return "不支持的日程操作"
# ---------- 工具5:计算器 ----------
def calculator(expr: str) -> str:
try:
return str(eval(expr, {"__builtins__": {}}))
except Exception as e:
return f"计算错误:{e}"
TOOLS = {
"weather": get_weather,
"news": search_news,
"email": draft_email,
"calendar": manage_schedule,
"calculator": calculator,
}
TOOLS_DESC = """
- weather[city]: 查询城市天气,参数为城市名
- news[keyword]: 搜索新闻,参数为关键词
- email[recipient|subject|hint]: 起草邮件,参数用|分隔:收件人|主题|内容要点
- calendar[action|date|event]: 日程管理,action为add/list/update,参数用|分隔
- calculator[expr]: 数学计算
"""
注意邮件工具的设计:只起草不发送。有真实副作用的操作留给人工确认,这是第72篇安全原则的直接应用。
2.2 agent.py:多轮任务规划
import re
from langchain_openai import ChatOpenAI
REACT_PROMPT = """你是一个私人助理,可以使用以下工具帮用户完成任务:
{tools}
用户的一句话可能包含多个子任务(比如"查天气,如果下雨就改日程")。
请逐个处理,严格按以下格式:
Thought: 分析当前要做什么
Action: 工具名[参数]
Observation: (工具返回结果,不要编造)
全部子任务完成后:
Thought: 所有任务已完成
Final Answer: 给用户一份完整的、自然语言的总结(包含所有子任务的结果)
历史记录:
{scratchpad}
用户请求:{question}
"""
class AssistantAgent:
def __init__(self, max_steps=12):
self.llm = ChatOpenAI(model="gpt-4", temperature=0)
self.max_steps = max_steps
def run(self, question: str) -> str:
scratchpad = ""
for _ in range(self.max_steps):
resp = self.llm.invoke(REACT_PROMPT.format(
tools=TOOLS_DESC, question=question, scratchpad=scratchpad
)).content
if "Final Answer:" in resp:
return resp.split("Final Answer:")[-1].strip()
m = re.search(r"Action:\s*(\w+)\[(.*?)\]", resp, re.DOTALL)
if not m:
scratchpad += resp + "\n"
continue
tool_name, raw_args = m.group(1), m.group(2).strip()
args = [a.strip() for a in raw_args.split("|")]
try:
obs = TOOLS[tool_name](*args) if tool_name in TOOLS \
else f"未知工具:{tool_name}"
except TypeError:
obs = f"工具{tool_name}参数格式错误"
scratchpad += f"{resp}\nObservation: {obs}\n"
return "任务过于复杂,超过最大执行步数"
复合指令"如果下雨就改日程"的执行轨迹:
Step1: Thought: 先查明天北京天气 → weather[北京]
→ Observation: 明天小雨,降雨概率85%
Step2: Thought: 下雨概率很高,需要把户外跑步改为室内
→ calendar[update|2024-11-16|户外跑步→室内健身]
→ Observation: 日程已更新
Step3: Thought: 最后起草邮件通知教练
→ email[coach@gym.com|训练调整|明天因雨改为室内训练]
→ Observation: 邮件草稿已生成
Final Answer: 明天北京有小雨(降雨概率85%),已为您将...
2.3 app.py:Streamlit界面
import streamlit as st
from agent import AssistantAgent
st.set_page_config(page_title="AI私人助理", page_icon="🤖")
st.title("🤖 AI私人助理")
if "messages" not in st.session_state:
st.session_state.messages = []
if "agent" not in st.session_state:
st.session_state.agent = AssistantAgent()
# 渲染历史消息
for msg in st.session_state.messages:
with st.chat_message(msg["role"]):
st.markdown(msg["content"])
# 处理新输入
if prompt := st.chat_input("试试:查明天北京天气,下雨就把跑步改室内"):
st.session_state.messages.append({"role": "user", "content": prompt})
with st.chat_message("user"):
st.markdown(prompt)
with st.chat_message("assistant"):
with st.spinner("助理正在处理..."):
answer = st.session_state.agent.run(prompt)
st.markdown(answer)
st.session_state.messages.append({"role": "assistant", "content": answer})
运行:streamlit run app.py,浏览器打开即是一个完整的聊天式助理。
3 ~> 从这个实战中学到什么
| 知识点 | 来源篇章 | 在实战中的应用 |
|---|---|---|
| ReAct循环 | 第66篇 | agent.py的核心执行循环 |
| 工具描述规范 | 第67篇 | TOOLS_DESC的参数格式约定 |
| 安全原则 | 第72篇 | 邮件只起草不发送 |
| 熔断保护 | 第72篇 | max_steps=12 |
| 界面搭建 | 第44篇 | Streamlit会话管理 |
思考 && 总结
- 复合指令是私人助理的分水岭: 单一指令的Agent是工具调用器,能拆解"如果A就B"的Agent才是助理。这依赖Prompt中明确要求"逐个处理子任务"。
- 工具间的参数约定要统一: 本文用
|分隔多参数,比让LLM生成JSON更不容易出错——LLM对分隔符的遵循率远高于结构化输出。 - 真实副作用操作留人工关卡: 邮件起草而非发送,看似功能缩水,实则是能上线和不能上线的分界线。
- Streamlit让Demo成本趋近于零: 不到50行代码就有一个能分享给别人试用的界面。
这个项目骨架可以直接扩展:把天气换成真实API、日程换成Google Calendar、邮件接上SMTP(加确认环节)——它就是一个能真正为你干活的助理。
结尾
各位小伙伴,本文的内容到这里就全部结束了,源码骑士在这里再次感谢您的阅读!
源码骑士 — Android Framework & 全栈开发
👀 关注:跟博主一起从源码视角深耕底层原理,见证每一次成长
❤️ 点赞:让优质内容被更多人看见,让知识传递更有力量
⭐ 收藏:把核心知识点存好,在需要时随时查、随时用
💬 评论:分享你的经验或疑问,评论区一起交流避坑
🔄 一键四连:不要忘记给博主"一键四连"哦!
🗡️ 寄语:技术之路难免有困惑,但同行的人会让前进更有方向
结语:Agent板块到这里收官。从ReAct到多Agent协作再到这个实战助理,你已经掌握了2025年AI开发最热的技能树。下一板块我们进入大模型微调的世界——从"用好模型"到"定制模型"。不要忘记给博主"一键四连"哦!
更多推荐
所有评论(0)