【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 ~> 整体架构

Streamlit 聊天界面

Agent 核心
ReAct循环

工具路由

weather
天气查询

news
新闻搜索

email
邮件起草

calendar
日程读写

calculator
通用计算

结果串联
生成最终回复

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会话管理

思考 && 总结

  1. 复合指令是私人助理的分水岭: 单一指令的Agent是工具调用器,能拆解"如果A就B"的Agent才是助理。这依赖Prompt中明确要求"逐个处理子任务"。
  2. 工具间的参数约定要统一: 本文用|分隔多参数,比让LLM生成JSON更不容易出错——LLM对分隔符的遵循率远高于结构化输出。
  3. 真实副作用操作留人工关卡: 邮件起草而非发送,看似功能缩水,实则是能上线和不能上线的分界线。
  4. Streamlit让Demo成本趋近于零: 不到50行代码就有一个能分享给别人试用的界面。

这个项目骨架可以直接扩展:把天气换成真实API、日程换成Google Calendar、邮件接上SMTP(加确认环节)——它就是一个能真正为你干活的助理。


结尾

各位小伙伴,本文的内容到这里就全部结束了,源码骑士在这里再次感谢您的阅读!

源码骑士 — Android Framework & 全栈开发

👀 关注:跟博主一起从源码视角深耕底层原理,见证每一次成长

❤️ 点赞:让优质内容被更多人看见,让知识传递更有力量

收藏:把核心知识点存好,在需要时随时查、随时用

💬 评论:分享你的经验或疑问,评论区一起交流避坑

🔄 一键四连:不要忘记给博主"一键四连"哦!

🗡️ 寄语:技术之路难免有困惑,但同行的人会让前进更有方向

结语:Agent板块到这里收官。从ReAct到多Agent协作再到这个实战助理,你已经掌握了2025年AI开发最热的技能树。下一板块我们进入大模型微调的世界——从"用好模型"到"定制模型"。不要忘记给博主"一键四连"哦!

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐