数据中心天气代理

一个基于LangGraph的代理系统,通过Model Context Protocol (MCP)确定数据中心位置的天气预报。

  • by devrodrigoreis
  • Python
  • 本地部署
  • 更新于
在 GitHub 查看 devrodrigoreis/datacenter_weather_agent

使用说明

数据中心气象代理

一种LangGraph代理系统,使用模型上下文协议(MCP)确定数据中心位置的天气预报。

建筑:自定义状态图,具有显式状态管理、模块化节点和符合LangGraph 2026最佳实践的条件路由。

概述

该系统回答了以下问题: “数据中心的天气预报是什么?”

它自主地: 0.检查意图分类,避免回答不应该回答的问题,或对任何问题给出相同的答案

  1. 发现数据中心的公共IP地址
  2. 将IP解析为地理坐标
  3. 获取该位置的当前天气预报
  4. 合成自然语言响应

为什么是这种架构?

此实现使用LangGraph的 自定义状态图 方法而不是预构建的代理,因为:

  • 完全透明:每个决策点都是明确的,可调试的
  • 生产就绪:模块化设计支持测试、监控和扩展
  • 最佳实践:遵循LangGraph 2026对LangGraph文档中获取的有状态代理的建议:https://docs.langchain.com/oss/python/langgraph/workflows-agents
  • 可维护性:与类型化状态模式明确分离关注点

系统架构

┌─────────────────────────────────────────────────────────────────┐
│                    Data Center Weather Agent                    │
│                                                                 │
│  ┌─────────────┐         ┌───────────────────────────────────┐  │
│  │   Agent     │  HTTP   │         MCP Server                │  │
│  │ (LangGraph) │◄───────►│  ┌────────┐ ┌─────────┐ ┌───────┐ │  │
│  │             │   SSE   │  │ ipify  │ │ip_to_geo│ │weather│ │  |
│  │  5 Nodes    │         │  └───┬────┘ └────┬────┘ └──┬────┘ │  │
│  │  3 Edges    │         │      │           │         │      │  │
│  │  9 State    │         │      ▼           ▼         ▼      │  │
│  │  Fields     │         │    ipify.org  ip-api.com  meteo   │  │
│  └─────────────┘         └───────────────────────────────────┘  │
└─────────────────────────────────────────────────────────────────┘

组件

1.MCP服务器(server/)

  • 技术:Python、FastMCP、uvicorn
  • 端口:8000(苏格兰和南方能源公司运输)
  • 工具:
    • ipify:获取公共IP地址
    • ip_to_geo:将IP转换为lat/lon(IP-api.com)
    • weather_forecast:获取天气数据(Open Meteo)
  • 特性:输入验证、错误处理、结构化日志记录

2.LangGraph代理(agent/)

  • 技术:Python、LangGraph、LangChain、谷歌Gemini
  • 建筑:自定义状态图
  • 状态:跟踪工作流进度的9个键入字段
  • 节点:5个模块化计算单元
  • 边缘:3个条件路由函数
  • 特性:LLM回退、详细跟踪、错误恢复

状态管理

所有数据都通过强类型 AgentState:

class AgentState(TypedDict):
    question: str              # User's input query
    public_ip: str | None      # From ipify tool
    latitude: float | None     # From ip_to_geo tool  
    longitude: float | None    # From ip_to_geo tool
    weather_data: str | None   # From weather_forecast tool
    answer: str | None         # Final LLM response
    messages: list             # Conversation history
    error: str | None          # Error tracking
    current_step: str          # Progress indicator

图形结构

START
  ↓
┌───────────────────────┐
│  intent classification│ → [custom logic]
└──────┬────────────────┘
       ↓
┌─────────────┐
│  get_ip     │ → [ipify tool]
└──────┬──────┘
       │ [conditional: success/error]
       ↓
┌──────────────────┐
│ resolve_location │ → [ip_to_geo tool]
└──────┬───────────┘
       │ [conditional: success/error]
       ↓
┌──────────────┐
│fetch_weather │ → [weather_forecast tool]
└──────┬───────┘
       │ [conditional: success/error]
       ↓
┌────────────────┐
│generate_answer │ → [LLM synthesis]
└───────┬────────┘
        ↓
       END

     [error] ← Any failure routes here

节点职责

  1. get_ip_node:调用ipify工具,使用public_ip更新状态
  2. resolve_location_node:调用ip_to_geo,提取lat/lon
  3. fetch_weather_node:使用坐标调用weather_forecast
  4. generate_answer_node:LLM创建自然语言响应
  5. 错误节点:集中错误处理和用户消息传递

条件路由

在继续之前,每条边都会验证之前的操作:

def route_after_ip(state: AgentState) -> Literal["resolve_location", "error"]:
    """Route to next node or error based on IP fetch result"""
    if state.get("error") or not state.get("public_ip"):
        return "error"
    return "resolve_location"

设置

先决条件

  • Python 3.10或更高版本
  • 在3.14.2版本上进行了全面测试
  • Google Gemini API密钥(提供免费层)
  • 可选:LongCat API密钥(回退)

安装

  1. 克隆或下载 这个项目

  2. 安装依赖项:

    pip install -r requirements.txt
    
  3. 配置API密钥:

    复制示例环境文件:

    cp .env.example .env
    

    编辑 .env 并添加您的API密钥:

    GOOGLE_API_KEY=your_gemini_api_key_here
    LONGCAT_API_KEY=your_longcat_api_key_here  # Optional
    

    获取API密钥:

    • 双子座:https://ai.google.dev/
    • 长猫:https://longcat.chat/(可选回退)

用法

启动系统

您需要两个终端窗口:

终端1-启动MCP服务器:

python3 -m server.main

预期产量:

INFO:     Started server process
INFO:     Waiting for application startup.
INFO:     Application startup complete.
INFO:     Uvicorn running on http://0.0.0.0:8000

终端2-运行代理:

python3 -m agent.main

示例会话

============================================================
Data Center Weather Agent (Custom StateGraph)
============================================================

Connected to MCP Server
LLM configured with fallback: Gemini -> LongCat

Graph structure built successfully

Agent Ready! (Type 'quit' to exit)
------------------------------------------------------------

Enter your question: What is the weather forecast of the data center?

============================================================
EXECUTION TRACE
============================================================

[Step 1: IP Discovery]
  Tool: ipify
  Result: 174.162.142.78

[Step 2: Location Resolution]
  Tool: ip_to_geo
  Input: 174.162.142.78
  Result: 40.3495, -111.8998

[Step 3: Weather Retrieval]
  Tool: weather_forecast
  Input: lat=40.3495, lon=-111.8998
  Result: Temperature: 0.2 C, Windspeed: 2.2 km/h

[Step 4: Answer Generation]
  Generated answer successfully

============================================================
FINAL ANSWER
============================================================

The data center, located at 40.3495°N, 111.8998°W, currently has a 
temperature of 0.2°C and a wind speed of 2.2 km/h.

Enter your question: Where is the data center located?

[... processes IP and location lookup ...]

FINAL ANSWER

The data center is located at coordinates 40.3495°N, 111.8998°W 
(approximately in Utah, United States).

Enter your question: quit

文件结构

datacenter_weather_agent/
├── server/
│   ├── main.py              # MCP server with FastMCP
│   ├── tools.py             # Tool implementations
│   └── __init__.py
├── agent/
│   ├── main.py              # Custom StateGraph agent (~570 lines)
│   ├── client.py            # MCP client wrapper
│   └── __init__.py
├── README.md                # This file
├── ARCHITECTURE.md          # Deep technical documentation
├── .env.example             # API key template
├── .env                     # Your API keys (create this)
└── requirements.txt         # Python dependencies

主要特点

1.稳健的输入验证

服务器端:

  • IP地址格式验证(IPv4/IPv6)
  • IPv4八位字节范围检查(0-255)
  • 坐标范围验证(纬度:-90至90,经度:-180至180)
  • 对所有参数进行类型检查

客户端:

  • 响应结构验证
  • 非空内容验证
  • 运行时错误检测

2.全面的错误处理

每个节点错误处理:

async def get_ip_node(state, tools):
    try:
        # ... tool execution ...
        return success_state
    except Exception as e:
        logger.error(f"Error in get_ip_node: {e}")
        return error_state

条件路由到错误节点:

  • 任何节点故障都会路由到集中式错误处理程序
  • 用户友好的错误消息
  • 代理保持响应(不崩溃)

3.LLM回退策略###->额外功能<-

根据速率限制自动故障切换:

gemini_llm = ChatGoogleGenerativeAI(model="gemini-3-pro-preview")
longcat_llm = ChatOpenAI(model="LongCat-Flash-Chat", ...)
llm = gemini_llm.with_fallbacks([longcat_llm])

4.详细的执行跟踪

每个操作都会被记录:

  • 带有输入和输出的工具调用
  • 状态转换
  • 错误条件
  • LLM推理

适合调试和监控。

5.清理执行跟踪

该代理通过以下方式产生干净、集中的输出:

  • 抑制详细的库日志(httpx、google_genai)
  • 仅显示基本步骤信息
  • 自动以静默方式处理LLM回退

面向用户的跟踪只显示重要的内容:

[Step 1: IP Discovery]
  Tool: ipify
  Result: 174.162.142.78

后端日志仍然可以捕获调试的详细信息。

工具调用策略

代理人执行 严格的顺序工作流程:

  1. IP发现 (ipify)

    • 无先决条件
    • 必须成功才能继续
  2. 位置分辨率 (ip_to_geo)

    • 要求: public_ip 从步骤1
    • 调用前验证IP格式
    • 从响应解析lat/lon
  3. 天气检索 (天气预报)

    • 要求: latitude 和 longitude 从步骤2
    • 验证坐标范围
    • 返回格式化的天气字符串
  4. 答案生成 LLM

    • 要求:所有收集的数据
    • 合成自然语言响应
    • 维护对话上下文

验证:每个条件边在路由到下一个节点之前验证先决条件是否存在。

生产增强

启用检查点(对话内存)

这是对制作环境的额外意图。

from langgraph.checkpoint.sqlite import SqliteSaver

# In build_graph():
async with SqliteSaver.from_conn_string("./checkpoints.db") as memory:
    graph = workflow.compile(checkpointer=memory)

# In main():
config = {"configurable": {"thread_id": "user-123"}}
result = await graph.ainvoke(state, config=config)

在循环中添加人类

from langgraph.checkpoint.sqlite import SqliteSaver

# Compile with interrupt
graph = workflow.compile(
    checkpointer=memory,
    interrupt_before=["generate_answer"]  # Pause before final answer
)

# Resume after approval
graph.invoke(None, config=config)  # Continue from interrupt

多代理扩展

创建专门的子代理:

weather_specialist = build_weather_subgraph()
location_specialist = build_location_subgraph()

workflow.add_node("weather", weather_specialist)
workflow.add_node("location", location_specialist)
workflow.add_conditional_edges("start", route_to_specialist, {...})

故障排除

服务器连接失败

症状: connection refused 或 server not found

解决方案:

  1. 验证服务器是否正在运行: curl http://localhost:8000/sse
  2. 检查没有其他进程正在使用端口8000
  3. 查看服务器日志以查找启动错误

LLM速率限制错误

症状: 429 RESOURCE_EXHAUSTED

解决方案:

  • 如果配置,回退到LongCat会自动触发
  • 等待60秒,然后重试
  • 考虑升级到Gemini付费等级

导入错误

症状: ModuleNotFoundError

解决方案:

# Always run from project root:
cd /path/to/datacenter_weather_agent
python3 -m agent.main 

工具验证错误

症状: Invalid IP address format 或 Invalid latitude

解决方案:

  • 检查服务器日志以了解详细错误
  • 验证外部API是否可访问
  • 手动测试: curl https://api.ipify.org?format=json

演出

典型指标:

  • 冷启动:1-2秒(库加载)
  • 工具执行:3-5秒(3×HTTP请求)
  • LLM合成:0.5-1秒
  • 总计:每次查询约5-8秒
  • 内存:~50MB运行时

瓶颈:

  • 外部API潜伏时间(ipify、ip-API、open-metao)
  • LLM生成无时间层api密钥的性能明显较慢,请耐心等待
  • 网络条件

局限性

  1. 顺序执行:工具按顺序运行(在某些情况下可以并行运行)
  2. 仅限公共IP:无法确定内部/私有IP的位置
  3. 支持VPN:位置反映VPN退出节点(如果活动)
  4. 无重试逻辑:失败的工具调用立即出错(可以添加指数回退)
  5. 单一查询:一次处理一个问题(可以添加批量支持)
  6. 没有坚持:会话之间的状态已清除(除非启用了检查点)

扩展代理

添加新工具

  1. 实施中 server/tools.py:

    async def get_timezone(latitude: float, longitude: float) -> str:
        # Implementation
        return timezone_data
    
  2. 注册于 server/main.py:

    @mcp.tool()
    async def timezone(latitude: float, longitude: float) -> str:
        return await get_timezone(latitude, longitude)
    
  3. 添加节点 agent/main.py:

    async def get_timezone_node(state, tools):
        tool = next(t for t in tools if t.name == "timezone")
        result = await tool.ainvoke({
            "latitude": state["latitude"],
            "longitude": state["longitude"]
        })
        return {**state, "timezone": result}
    
    workflow.add_node("get_timezone", get_timezone_node)
    workflow.add_edge("fetch_weather", "get_timezone")
    workflow.add_edge("get_timezone", "generate_answer")
    

修改路由逻辑

更改条件边以支持替代流:

def route_after_ip(state: AgentState):
    if state.get("error"):
        return "error"
    
    # New: Check if IP is internal/private
    ip = state.get("public_ip", "")
    if ip.startswith("192.168.") or ip.startswith("10."):
        return "handle_private_ip"  # New node
    
    return "resolve_location"

测试

单元测试

测试单个节点:

import pytest
from agent.main import get_ip_node

@pytest.mark.asyncio
async def test_get_ip_node():
    mock_tools = [MockIPifyTool()]
    state = {"question": "test", "messages": []}
    
    result = await get_ip_node(state, mock_tools)
    
    assert result["public_ip"] is not None
    assert result["error"] is None

集成测试

测试完整图形执行:

@pytest.mark.asyncio
async def test_full_workflow(mcp_server_running):
    graph = await build_graph(client, llm)
    initial_state = {...}
    
    final_state = await graph.ainvoke(initial_state)
    
    assert final_state["answer"] is not None
    assert "temperature" in final_state["answer"].lower()

LangSmith监控(来源于文档,最后访问时间为2026年1月30日)

启用生产调试跟踪:

export LANGCHAIN_TRACING_V2=true
export LANGCHAIN_API_KEY=your_key
export LANGCHAIN_PROJECT="datacenter-weather"

每次执行都会在LangSmith仪表板上进行跟踪。

安全考虑

  1. API密钥:从不承诺 .env 到版本控制
  2. 服务器暴露:MCP服务器没有身份验证(仅限本地使用)
  3. 速率限制:外部API可能会禁止滥用
  4. 输入验证:服务器验证所有工具输入
  5. 错误消息:避免日志中的敏感数据泄露

依赖项

  • httpx:异步HTTP客户端
  • 主控程序:模型上下文协议SDK
  • 兰格拉夫:基于图的代理框架
  • 语言链:LLM抽象层
  • 郎链谷歌genai:Gemini集成
  • 兰开夏:OpenAI兼容API(LongCat)
  • python dotenv:环境变量管理
  • 优维康:ASGI服务器

看 requirements.txt 对于特定版本。

了解更多

许可证

这是一个用于个人目的的示范项目。不提供保修。


建于:LangGraph自定义状态图(生产最佳实践,2026年1月)