项目背景与价值

在数字化时代,天气信息已成为人们日常决策的重要依据。传统的天气应用多停留在数据展示层面,而智能天气助手则通过AI技术实现了从数据提供到智能服务的跃迁。本项目基于openJiuwen Agent Core框架,构建了一个能够理解自然语言、提供个性化建议、具备智能决策能力的天气助手。

项目地址:https://atomgit.com/openJiuwen?utm_source=csdn

项目环境搭建

环境要求

  • Python 3.11+
  • pip包管理器

安装依赖

# 克隆项目
git clone https://atomgit.com/openJiuwen/agent-core
cd agent-core

# 创建虚拟环境
python -m venv venv
source venv/bin/activate

# 安装依赖
pip install -e .

配置环境变量

在项目根目录创建.env文件:

API_BASE=xxx
API_KEY=xxxxx
MODEL_NAME=xxxx
MODEL_PROVIDER=openai

核心技术栈与架构

openJiuwen框架选择

我选择openJiuwen Agent Core作为开发框架,主要基于以下考虑:

  • ReAct架构支持:支持推理与行动的智能体模式
  • 工具集成能力:灵活的外部API集成机制
  • 对话管理:强大的上下文理解与管理能力
  • 可扩展性:模块化设计便于功能扩展

ReAct架构详解

ReAct(Reasoning and Acting)架构是本项目的核心,它允许智能体在推理(制定计划、进行逻辑思考)和行动(与外部工具交互)之间交替进行。这种架构特别适合需要外部知识和工具的复杂任务。

在智能天气助手中,ReAct架构的工作流程如下:

  1. 观察(Observation):接收用户输入,如"明天杭州天气如何?"
  2. 推理(Thought):分析用户意图,确定需要调用天气API
  3. 行动(Action):调用WeatherReporter工具查询天气
  4. 观察(Observation):获取天气API返回的结果
  5. 推理(Thought):分析天气数据,准备响应
  6. 最终响应(Final Response):向用户提供天气信息
# ReAct智能体的核心工作流程
class ReActAgent:
    def invoke(self, input_data):
        # 初始化对话历史
        history = []
        
        # 添加用户输入
        history.append({"role": "user", "content": input_data["query"]})
        
        # 循环直到任务完成
        for iteration in range(MAX_ITERATIONS):
            # 使用LLM生成下一步动作
            response = self.model.generate(history)
            
            # 解析LLM输出,判断是工具调用还是最终响应
            if self._is_tool_call(response):
                # 执行工具调用
                tool_result = self._execute_tool(response)
                
                # 将工具结果添加到历史
                history.append({"role": "tool", "content": tool_result})
            else:
                # 返回最终结果
                return {"result": response}
        
        return {"result": "任务超时或无法完成"}

在实际的ReAct循环中,智能体能够:

  • 分析用户查询的意图
  • 决定是否需要调用外部工具
  • 解析工具返回的结果
  • 生成自然语言响应
  • 处理错误和异常情况

系统架构设计

┌─────────────────┐    ┌──────────────────┐    ┌─────────────────┐
│   用户输入      │───▶│   ReAct智能体    │───▶│   响应生成      │
│   (自然语言)    │    │   (决策引擎)     │    │   (格式化)      │
└─────────────────┘    └──────────────────┘    └─────────────────┘
                           │
                           ▼
                    ┌──────────────────┐
                    │   天气API工具    │
                    │   (30日预报)     │
                    └──────────────────┘

模块化实现详解

工具模块

工具模块负责与外部API交互,提供天气数据查询功能。

天气查询工具实现

def build_weather_tool():
    import requests
    import json
    
    def get_weather_info(location: str = "", date: str = ""):
        """通过一刻天气API获取30日天气信息"""
        try:
            # 如果date为空,使用当前日期
            if not date or date.strip() == "":
                from datetime import datetime
                date = datetime.now().strftime("%Y-%m-%d")
            
            # 如果location为空字符串或None,使用空值让API自动根据IP定位
            city_param = location if location and location.strip() != "" else ""
            url = f"https://v1.yiketianqi.com/free/month?appid=xxxx&appsecret=xxxx&unescape=1&city={city_param}"
            response = requests.get(url, timeout=10)
            
            if response.status_code == 200:
                data = response.json()
                if 'errcode' in data and data['errcode'] == 100:
                    # API返回错误,errcode为100表示错误
                    return {"error": f"无法获取天气信息: {data.get('errmsg', '未知错误')}"}
                else:
                    # 成功获取天气数据,查找指定日期的天气
                    target_date_weather = None
                    for day_data in data.get('data', []):
                        if day_data.get('date') == date:
                            target_date_weather = day_data
                            break
                    
                    if target_date_weather:
                        # 找到指定日期的天气数据
                        weather_info = {
                            "city": data.get("city", location or "当前定位"),
                            "date": date,
                            "weather": target_date_weather.get("wea", "未知"),
                            "temperature": f"最高{target_date_weather.get('tem_day', '?')}°C, 最低{target_date_weather.get('tem_night', '?')}°C",
                            "wind": f"{target_date_weather.get('win', '未知')} {target_date_weather.get('win_speed', '未知')}",
                            "all_data": data  # 保留所有数据,以备需要
                        }
                        return weather_info
                    else:
                        # 如果没有找到指定日期,返回未来7天的天气预报,尽管API返回30天数据但实际代码中使用forecast_7days字段
                        forecast_list = []
                        for day_data in data.get('data', []):
                            forecast_list.append({
                                "date": day_data.get("date"),
                                "weather": day_data.get("wea", "未知"),
                                "temperature": f"最高{day_data.get('tem_day', '?')}°C, 最低{day_data.get('tem_night', '?')}°C",
                                "wind": f"{day_data.get('win', '未知')} {day_data.get('win_speed', '未知')}"
                            })
                        
                        weather_info = {
                            "city": data.get("city", location or "当前定位"),
                            "date": date,
                            "weather": "未找到指定日期的天气数据",
                            "temperature": "N/A",
                            "wind": "N/A",
                            "forecast_7days": forecast_list,  # 实际代码中是forecast_7days,尽管API返回30天数据
                            "all_data": data
                        }
                        return weather_info
            else:
                return {"error": f"API请求失败,状态码: {response.status_code}"}
        except Exception as e:
            return {"error": f"获取天气信息时发生错误: {str(e)}"}

    from openJiuwen.core.utils.tool.function.function import LocalFunction
    from openJiuwen.core.utils.tool.param import Param
    
    return LocalFunction(
        name="WeatherReporter",
        description="天气查询插件,可以查询指定地点未来30天的天气信息,当城市为空时会根据IP自动定位",
        params=[
            Param(name="location", description="天气查询的地点,可为空(为空时将根据IP自动定位)", type="string", required=False),
            Param(name="date", description="天气查询的时间,格式为YYYY-MM-DD", type="string", required=True),
        ],
        func=get_weather_info
    )

工具模块特点:

  • 实现了完整的错误处理机制
  • 支持自动IP定位功能,支持用户免说地点,交互更加自然。
  • 提供了数据格式标准化
  • 包含了超时处理机制

openJiuwen也支持使用@tool装饰器和封装Restful接口两种方式快速自定义工具,详见:agent-core开发指南->基础功能->自定义工具

模型配置模块

模型配置模块负责设置LLM参数和API连接信息。

模型配置实现

def build_model():
    return ModelConfig(
        model_provider=MODEL_PROVIDER,
        model_info=BaseModelInfo(
            model=MODEL_NAME,
            api_base=API_BASE,
            api_key=API_KEY,
            temperature=0.7,  # 控制生成的随机性,0.7提供平衡的创造性和一致性
            top_p=0.9,        # 控制输出的多样性,0.9保留最可能的90%词汇
            timeout=30,       # API调用超时时间
        ),
    )

模型配置是智能体性能的关键因素。在智能天气助手中,我精心调整了以下参数:

  • temperature (0.7): 这个值在创造性和一致性之间取得了良好平衡。对于天气助手,既需要一定的创造性来生成自然的响应,又需要保持信息的准确性。
  • top_p (0.9): 核采样参数,确保输出在保持自然性的同时不会过于随机。
  • timeout (30): 考虑到天气API响应和LLM推理的时间,设置30秒超时以确保用户体验。

模型,使用的是硅基流动提供的OpenAI兼容的API,这允许我利用成熟的LLM服务来实现复杂的推理和自然语言生成能力。模型需要理解自然语言查询、决定何时调用工具、处理工具返回的结果,并生成自然的响应。

提示词工程模块

提示词工程模块定义了智能体的行为规则和交互逻辑。

系统提示词设计

def build_prompt():
    template = (
        "你是智能天气助手,主要负责回答用户的天气查询及相关生活建议。"
        "1. 默认查今天({})的天气;"
        "2. 调用工具时,直接使用中文城市名,例如:北京、上海、广州等;"
        "3. 城市参数格式为中文城市名,如'北京'、'上海'、'广州'等;"
        "4. 如果用户没有明确指定城市,可以将城市参数留空,API会根据IP自动定位;"
        "5. 如果用户没有明确指定日期,使用今天;"
        "6. 当用户询问穿衣建议、出行建议或类似问题时,如果涉及天气相关,应先查询天气,然后根据天气情况给出建议;"
        "7. 如果用户的问题与天气关系不大,可以不使用天气工具,直接回答用户问题;"
        "8. 对于穿衣建议类问题,如果用户没有指定城市,必须先自动定位查询本地天气,再给出穿衣建议。"
        "9. 对于不依赖天气就可以作答的问题,视为超出能力范畴,不予回答,并为用户介绍你的能力。"
    )
    today = datetime.now().strftime("%Y-%m-%d")
    return [{"role": "system", "content": template.format(today)}]

提示词工程的核心原则:

  • 定义了明确的工具调用规则
  • 规定了自动定位逻辑
  • 设置了智能决策条件
  • 确定了超出范围问题的处理方式

智能体核心模块

智能体核心模块整合了所有组件,创建完整的智能体实例。

智能体创建

def create_weather_agent():
    """
    创建天气查询智能体
    """
    from openJiuwen.core.utils.tool.param import Param
    from openJiuwen.core.utils.llm.base import BaseModelInfo
    from openJiuwen.core.component.common.configs.model_config import ModelConfig
    from openJiuwen.agent.react_agent import create_react_agent_config, ReActAgent
    from openJiuwen.agent.common.schema import PluginSchema
    from datetime import datetime
    
    # 1. 创建模型配置
    def build_model():
        return ModelConfig(
            model_provider=MODEL_PROVIDER,
            model_info=BaseModelInfo(
                model=MODEL_NAME,
                api_base=API_BASE,
                api_key=API_KEY,
                temperature=0.7,  # 控制生成的随机性,0.7提供平衡的创造性和一致性
                top_p=0.9,        # 控制输出的多样性,0.9保留最可能的90%词汇
                timeout=30,       # API调用超时时间
            ),
        )

    # 2. 创建提示词模板
    def build_prompt():
        template = (
            "你是智能天气助手,主要负责回答用户的天气查询及相关生活建议。"
            "1. 默认查今天({})的天气;"
            "2. 调用工具时,直接使用中文城市名,例如:北京、上海、广州等;"
            "3. 城市参数格式为中文城市名,如'北京'、'上海'、'广州'等;"
            "4. 如果用户没有明确指定城市,可以将城市参数留空,API会根据IP自动定位;"
            "5. 如果用户没有明确指定日期,使用今天;"
            "6. 当用户询问穿衣建议、出行建议或类似问题时,如果涉及天气相关,应先查询天气,然后根据天气情况给出建议;"
            "7. 如果用户的问题与天气关系不大,可以不使用天气工具,直接回答用户问题;"
            "8. 对于穿衣建议类问题,如果用户没有指定城市,必须先自动定位查询本地天气,再给出穿衣建议。"
            "9. 对于不依赖天气就可以作答的问题,视为超出能力范畴,不予回答,并为用户介绍你的能力。"
        )
        today = datetime.now().strftime("%Y-%m-%d")
        return [{"role": "system", "content": template.format(today)}]

    # 3. 创建Agent配置
    agent_config = create_react_agent_config(
        agent_id="weather_assistant",
        agent_version="0.1.0",
        description="Jiuwen 智能天气助手 - 不仅提供天气查询,还能根据天气情况提供穿衣建议、出行提示等智能服务",
        model=build_model(),
        prompt_template=build_prompt(),
    )
    
    # 4. 创建ReAct Agent实例
    weather_agent = ReActAgent(agent_config)
    
    # 5. 添加天气查询工具
    weather_agent.add_tools([build_weather_tool()])
    
    return weather_agent

效果展示

智能体运行起来,有一个提示信息展示和简单的交互界面,等待输入问题。

天气查询

直接问天气

如下图所示,问题是上海明天的天气。可以看到,能够正常调用天气查询工具,并且工具成功返回了了目标城市为上海的天气,最后智能体调用大模型,将api返回的天气进行整理润色,并给出了一些总结建议。

不带位置问天气

也支持不带地理位置直接问天气情况。如下图所示,问题是明天天气如何?可以看到,也是能够正常调用天气查询工具,并且自动识别出目标城市为杭州。

穿衣建议

针对问题:下周一上班,穿什么衣服合适,智能体的响应结果如下所示:

出行建议

针对问题:我下周要去深圳出差,给我一些出行建议,智能体的响应结果如下所示:

针对问题:周末适合户外活动吗?想带娃出去逛一逛。,智能体的响应结果如下所示:

项目总结

智能天气助手项目展示了AI技术在传统服务领域的应用价值。通过openJiuwen Agent Core框架,我成功构建了一个具备智能决策、自然语言理解、个性化服务等能力的智能助手。

项目的核心价值在于:

  • 智能化升级:从数据查询到智能服务的跃迁
  • 用户体验优化:自然语言交互,个性化服务
  • 技术架构先进:ReAct架构,模块化设计
  • 扩展性强:支持多种应用场景和功能扩展

通过本项目,我验证了ReAct架构在构建智能助手方面的有效性,展示了如何将简单的天气API数据转化为对用户有价值的智能服务。随着AI技术的不断发展,智能天气助手将为用户提供更加智能、便捷、个性化的服务体验。

关于openJiuwen的更多信息,可参考:

更多推荐