import requests
from typing import Dict, Any, TypedDict
from langchain_core.tools import tool
from langgraph.graph import StateGraph, END
from openai import OpenAI
from langchain_openai import ChatOpenAI # 大模型接口封装类
from langgraph.prebuilt import ToolNode # ToolNode封装好的工具执行节点
import re
import json
# 加载环境变量
# load_dotenv()
# 初始化 ChatOpenAI 实例
# # todo:1 实例化mcp_client客户端
# # 1.使用openai库调用大模型
# client = OpenAI(
# api_key="gpustack_078939279603f717_9b9d51de18ee165f66f6c32eda290f08", # 如果有认证
# base_url="http://10.191.39.211/v1" # 内部部署的模型API地址
# )
# # print("client-->",client)
#
# # 2.实例化模型
# model=ChatOpenAI(api_key="gpustack_078939279603f717_9b9d51de18ee165f66f6c32eda290f08", # 如果有认证
# base_url="http://10.191.39.211/v1", # 内部部署的模型API地址
# model="Qwen3-32B",temperature=0)
# # print("model-->",model)
# response = client.chat.completions.create(
# model="Qwen3-32B",
# messages=[{"role": "user", "content": "你好"}]
# )
# # print("response-->",response)
# todo:2 实例化mcp_server服务器
# todo:3 创建mcp工具节点函数
# LangChain工具:获取初始URL的JSON数据
# @tool # 注册工具函数
def fetch_initial_data(initial_url: str) -> Dict[str, Any]:
"""通过http get 请求,获取初始URL的JSON数据并提取error_code"""
try:
# 发送请求,获取响应对象
response = requests.get(initial_url)
# 检查http响应状态码,有问题的话抛出httperror异常
response.raise_for_status()
# 从返回的字典中拿值:response--》object,result-->dict
# print("response-->",response)
# print("response类型-->",type(response))
result = response.json()
# print("result-->",result)
# print("result-->", type(result))
if "error_code" not in result["data"][0].keys():
raise ValueError("JSON中缺少error_code字段")
return {"error_code": result["data"][0]["error_code"]}
except requests.RequestException as e:
raise Exception(f"获取初始数据失败: {str(e)}")
# LangChain工具:拼接新URL
@tool # 注册工具函数
def construct_new_url(error_code:str) -> str:
"""根据error_code拼接新URL"""
base_url = "https://itms.luxsan-ict.com/magicdevapi/apipost/GMESErrorCodeDetail"
# error_code=error_code.values()
return f"{base_url}?error_code={error_code}"
# LangChain工具:获取新URL的JSON数据
@tool # 注册工具函数
def fetch_error_documents(new_url: str) -> str:
"""接收construct_new_url函数()返回的url作为输入,通过http请求重新去get新URL的JSON数据"""
try:
# 发送请求,获取响应对象
response = requests.get(new_url)
# 检查http请求的响应状态码,有异常抛出
response.raise_for_status()
# 从返回的字典中拿值:response--》object,result-->dict
# 获取response中返回"errorcode_document": "https://kk.luxsan-ict.com/doc/3754/?highlight=A0470",
result=response.json()
if "errorcode_document" not in result["data"][0].keys():
raise ValueError("JSON中缺少error_code字段")
errorcode_document=result["data"][0]["errorcode_document"]
# 创建正则取值
pattern1 = r"https://[^/]+/doc/(\d+)/"
# 将规则和errorcode_document中的url传入,返回're.Match'对象
match = re.search(pattern1, errorcode_document)
# 提取参数3754
extracted_number = match.group(1)
# print(f"提取结果: {extracted_number}") # 输出:3754
return {"errorcode_document":extracted_number}
except requests.RequestException as e:
raise Exception(f"获取错误详情失败: {str(e)}")
# LangChain工具:拼接最后的URL
@tool # 注册工具函数
def construct_last_url(errorcode_document: str) -> str:
"""根据error_code拼接新URL"""
base_url = "https://itms.luxsan-ict.com/kkapi/api/get_doc/?token=dad9237d1db7cdf3994d115960ebfd2947a8978275ccde7a1d4a9dd8&did"
return f"{base_url}={errorcode_document}"
@tool # 注册工具函数
def fetch_content(last_url: str) -> str:
"""接收construct_new_url函数()返回的url作为输入,通过http请求重新去get新URL的JSON数据"""
try:
# 发送请求,获取响应对象
response = requests.get(last_url)
# 检查http请求的响应状态码,有异常抛出
response.raise_for_status()
# 从返回的字典中拿值:response--》object,result-->dict
# 获取response中返回"content":
result=response.json()
if "content" not in result["data"].keys():
raise ValueError("JSON中缺少error_code字段")
content=result["data"]["content"]
return {"content":content}
except requests.RequestException as e:
raise Exception(f"获取错误详情失败: {str(e)}")
# # LangChain工具:提取关键字段
# @tool
# def extract_fields(data: Dict[str, Any]) -> Dict[str, Any]:
# """提取JSON中的关键字段(假设为error_code和description)"""
# return {
# "error_code": data.get("error_code", "未知"),
# "description": data.get("description", "无描述")
# }
# # todo:4 定义状态机state并实例化workflow
# TypedDict必须包含下列的键名
class WorkflowState(TypedDict):
initial_url: str # 初始的url
error_code: str # 解析出error_code键值
construct_url: str # 第二个url
extracted_number:str # 抽取出的did
last_url:str # 最后的url
extracted_content: Dict[str, Any] # 查询结果
# todo:5 创建节点和边
# 定义图构建器
workflow = StateGraph(WorkflowState)
# 1.创建节点
workflow.add_node("fetch_initial_data",fetch_initial_data)
workflow.add_node("construct_new_url",construct_new_url)
workflow.add_node("fetch_error_documents",fetch_error_documents)
workflow.add_node("construct_last_url",construct_last_url)
workflow.add_node("fetch_content",fetch_content)
# 2.创建边
workflow.set_entry_point("fetch_initial_data")
workflow.add_edge("fetch_initial_data","construct_new_url")
workflow.add_edge("construct_new_url","fetch_error_documents")
workflow.add_edge("fetch_error_documents","construct_last_url")
workflow.add_edge("construct_last_url","fetch_content")
workflow.add_edge("fetch_content",END)
# 3.编译图
app=workflow.compile()
# todo:5 agent工具整合
# todo:6 编译成app
if __name__ == '__main__':
initial_url="https://itms.luxsan-ict.com/magicdevapi/apipost/GMESMilData?error_code=A0470"
# result1=fetch_initial_data(initial_url) # dict {'error_code': 'A0470'}
# result2=construct_new_url(result1)
# result3=fetch_error_documents(result2)
# result4=construct_last_url(result3)
# result5=fetch_content(result4)
# extract_fields()
result5=app.invoke(initial_url)
print(result5)
print(type(result5))
上面是元代码,下面是执行结果,请分析原因并给出解决方案
E:\Project\AIProject_2025\Exmul\Scripts\python.exe E:\Project\AIProject_2025\Agent\Excel2Multi\searchcode\graph_workflow.py
Traceback (most recent call last):
File "E:\Project\AIProject_2025\Agent\Excel2Multi\searchcode\graph_workflow.py", line 185, in <module>
result5=app.invoke(initial_url)
^^^^^^^^^^^^^^^^^^^^^^^
File "E:\Project\AIProject_2025\Exmul\Lib\site-packages\langgraph\pregel\main.py", line 3026, in invoke
for chunk in self.stream(
^^^^^^^^^^^^
File "E:\Project\AIProject_2025\Exmul\Lib\site-packages\langgraph\pregel\main.py", line 2647, in stream
for _ in runner.tick(
^^^^^^^^^^^^
File "E:\Project\AIProject_2025\Exmul\Lib\site-packages\langgraph\pregel\_runner.py", line 162, in tick
run_with_retry(
File "E:\Project\AIProject_2025\Exmul\Lib\site-packages\langgraph\pregel\_retry.py", line 42, in run_with_retry
return task.proc.invoke(task.input, config)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "E:\Project\AIProject_2025\Exmul\Lib\site-packages\langgraph\_internal\_runnable.py", line 401, in invoke
ret = self.func(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^
File "E:\Project\AIProject_2025\Exmul\Lib\site-packages\langgraph\pregel\_write.py", line 87, in _write
self.do_write(
File "E:\Project\AIProject_2025\Exmul\Lib\site-packages\langgraph\pregel\_write.py", line 129, in do_write
write(_assemble_writes(writes))
^^^^^^^^^^^^^^^^^^^^^^^^
File "E:\Project\AIProject_2025\Exmul\Lib\site-packages\langgraph\pregel\_write.py", line 184, in _assemble_writes
if ww := w.mapper(w.value):
^^^^^^^^^^^^^^^^^
File "E:\Project\AIProject_2025\Exmul\Lib\site-packages\langgraph\graph\state.py", line 983, in _get_updates
raise InvalidUpdateError(msg)
langgraph.errors.InvalidUpdateError: Expected dict, got https://itms.luxsan-ict.com/magicdevapi/apipost/GMESMilData?error_code=A0470
For troubleshooting, visit: https://python.langchain.com/docs/troubleshooting/errors/INVALID_GRAPH_NODE_RETURN_VALUE
During task with name '__start__' and id '9b5212d7-f7e6-b0ea-b8ca-d7d404bce9e6'
Process finished with exit code 1