Langchain mcp 可视化界面
界面依赖包gradio6.13.0,langchain1.2.15,langchain-mcp-adapters0.2.2,langchain-openai1.1.12,langgraph-cli[inmem]0.4.21,代码importasyncioimportuuidfromtypingimportList,DictimportgradioasgrfromgradioimportChatMessagefromlangchain.agentsimportcreate_agentfromlangchain_core.messagesimportHumanMessage,AIMessage,ToolMessagefromlangchain_mcp_adapters.clientimportMultiServerMCPClientfromlanggraph.typesimportCommandfromlanggraph.checkpoint.memoryimportInMemorySaverfromlangchain.chat_modelsimportinit_chat_modelfromdotenvimportload_dotenv,find_dotenvimportos load_dotenv(find_dotenv())ZHIPU_API_KEYos.getenv(ZHIPU_API_KEY)# ModelScope 上的 Qwen3-32BOpenAI 接口兼容QWEN3_32BQwen/Qwen3-32B# 优先读取 ModelScope 的 API Key若未配置则回退到 OPENAI_API_KEY兼容常见环境API_KEYos.getenv(MODELSCOPE_API_KEY)oros.getenv(OPENAI_API_KEY)ormodelinit_chat_model(modelQWEN3_32B,model_provideropenai,base_urlhttps://api-inference.modelscope.cn/v1/,api_keyAPI_KEY,temperature0,# 通过 extra_body 传入思考/推理开关与流式选项content_blocks 将包含 reasoning 与 textextra_body{# 常见思考模式参数的并集未识别的键会被后端忽略enable_thinking:False},)# 网络搜索MCP服务端工具的配置zhipuai_mcp_server_config{url:https://open.bigmodel.cn/api/mcp/web_search/sse?AuthorizationZHIPU_API_KEY,transport:sse,}# https://modelscope.cn/mcp/servers/Joooook/12306-mcp 部署mcp_12306_server_config{url:https://mcp.api-inference.modelscope.net/xxx/sse,transport:sse}# https://modelscope.cn/mcp/servers/antvis/mcp-server-chart 部署mcp_chart_server_config{url:https://mcp.api-inference.modelscope.net/xxx/sse,transport:sse}# 创建一个MCP的客户端去连接mcp_clientMultiServerMCPClient({zhipuai_mcp_server_config:zhipuai_mcp_server_config,my12306_mcp_server_config:mcp_12306_server_config,chart_mcp_server_config:mcp_12306_server_config,})# 这些MCP服务端的工具只支持异步模式所以只能用MCP的异步模式去调用asyncdefcreate_new_agent():mcp_toolsawaitmcp_client.get_tools()# 获取MCP的所有toolsreturncreate_agent(model,toolsmcp_tools,system_prompt你是一个智能助手尽可能的调用工具回答用户的问题,checkpointerInMemorySaver()# 创建一个内存的保存器 保存对话上下文)agentasyncio.run(create_new_agent())# 配置参数包含会话IDconfig{configurable:{# 检查点由session_id访问thread_id:str(uuid.uuid4()),}}# res agent.invoke(input{messages: [HumanMessage(content你好)]}, configconfig)# print(res)defadd_message(chat_history,user_message):ifuser_message:chat_history.append({role:user,content:user_message})returnchat_history,gr.Textbox(valueNone,interactiveFalse)asyncdefsubmit_messages(chat_history:List[Dict]):流式处理消息的核心函数user_inputchat_history[-1][content]current_stateagent.get_state(config)full_response# 累积完整响应tool_calls[]# 记录工具调用# 处理中断恢复或正常消息inputsCommand(resume{answer:user_input})ifcurrent_state.nextelse{messages:[HumanMessage(contentuser_input)]}asyncforchunkinagent.astream(inputs,config,stream_mode[messages,updates],# 同时监听消息和状态更新):ifmessagesinchunk:formessageinchunk[1]:# 处理AI消息流式输出ifisinstance(message,AIMessage)andmessage.content:full_responsemessage.content# 更新最后一条消息而非追加ifchat_historyandisinstance(chat_history[-1],ChatMessage)andtitlenotinchat_history[-1].metadata:chat_history[-1].contentfull_responseelse:chat_history.append(ChatMessage(roleassistant,contentmessage.content))yieldchat_history# 处理工具调用消息elifisinstance(message,ToolMessage):tool_msgf 调用工具:{message.name}\n{message.content}chat_history.append(ChatMessage(roleassistant,contenttool_msg,metadata{title:f️ Used tool{message.name}}))yieldchat_history# 创建Gradio界面withgr.Blocks(title我的智能小秘书,themegr.themes.Soft(),css.system {color: #666; font-style: italic;}# 自定义系统消息样式)asdemo:# 聊天历史记录组件chatbotgr.Chatbot(height500,render_markdownTrue,# 支持Markdown格式line_breaksFalse# 禁用自动换行符)# 输入组件chat_inputgr.Textbox(placeholder请输入您的消息...,label用户输入,max_lines5,containerFalse)# 控制按钮withgr.Row():submit_btngr.Button(发送,variantprimary)clear_btngr.Button(清空对话)# 消息提交处理链msg_handlerchat_input.submit(fnadd_message,inputs[chatbot,chat_input],outputs[chatbot,chat_input],queueFalse).then(fnsubmit_messages,inputschatbot,outputschatbot,api_namechat_stream# API端点名称)# 按钮点击处理链btn_handlersubmit_btn.click(fnadd_message,inputs[chatbot,chat_input],outputs[chatbot,chat_input],queueFalse).then(fnsubmit_messages,inputschatbot,outputschatbot)# 清空对话clear_btn.click(fnlambda:[],inputsNone,outputschatbot,queueFalse)# 重置输入框状态msg_handler.then(lambda:gr.Textbox(interactiveTrue),None,[chat_input])btn_handler.then(lambda:gr.Textbox(interactiveTrue),None,[chat_input])if__name____main__:demo.launch()