langchain-MCP与Function Calling

大模型在理解用户自然语言的基础上,自动调用后端预定义的函数或 API

function call

Function Call 的局限性:只支持单步、单函数的调用,难以灵活应对多步骤、多工具协作的复杂业务需求

langchain function call

使用:@tool装饰器,是一个智能的代码生成器

函数分析

当在函数上添加@tool装饰器时,LangChain会自动分析这个Python函数。它会读取函数的签名信息,包括参数名称、参数类型注解、默认值等。同时,它还会提取函数的docstring文档字符串作为工具的描述信息。

自动转换

LangChain将Python类型注解自动转换为OpenAI Function Calling 所需的JSON Schema格式,比如Python的str类型会转换为JSON Schema的"type": "string",int类型转换为"type": "integer",List[str]转换为数组类型等。

工具对象创建

装饰器会创建一个tool对象,这个对象包含了工具的名称、描述、参数模式等信息,这个tool对象既保留了原始python函数的调用,也具备了OpenAI工具定义的所有必要信息。

运行时调用

当AI决定调用某个工具时,LangChain可以直接通过Tool对象的invoke方法调用原始的Python函数,无需额外的映射代码。

案例

from dotenv import load_dotenv
import os
from langchain_openai import ChatOpenAI
from langchain.tools import tool
from langchain_core.messages import HumanMessage, SystemMessage
from langchain_core.runnables import RunnableLambda

# 加载环境变量
load_dotenv()

# 定义@tool工具
# 通过langchain.tools包下的@tool装饰器,将函数注册为工具。这里简单注册两个无输入,只有字符串输出的工具
@tool
def who_are_you() -> str:
    """当被问到身份时,回答身份信息"""
    return "我是atguigu工具小助手"

@tool
def what_can_you_do() -> str:
    """当被问到功能时,回答功能信息"""
    return "我的功能是进行工具选择"

# 根据用户输入信息,构造系统信息与用户信息,后续传入链中作为提示词。
def format_messages(inputs):
    return [
        SystemMessage(content="你是一个工具助手。"),
        HumanMessage(content=inputs["question"])
    ]


def execute_tool_calls(result):
    tools = [who_are_you,what_can_you_do]
    
    # 如果执行了工具调用,则进入
    if result.tool_calls:
        # LLM会给工具传入列表,越匹配的工具位置越靠前。对于此类调用单一工具的情况,只需要取出列表中的第一项即为被选中的工具
        tool_call = result.tool_calls[0]
        print(f"调用工具: {tool_call['name']}")
        print(f"参数: {tool_call['args']}")
        
        # 从工具列表中选择与上述被选定工具进行名称匹配,输出名称和参数信息
        for tool in tools:
            if tool.name == tool_call['name']:
                output = tool.invoke(tool_call['args'])
                print(f"执行结果: {output}")
                return output
    else:
        print(f"未调用工具: {result.content}")
        return result.content


def main():
    api_key = os.getenv('DASHSCOPE_API_KEY')
    llm_model = os.getenv('QWEN_LLM_MODEL')
    base_url = os.getenv('BASE_URL')

    llm = ChatOpenAI(
        model=llm_model,
        api_key=api_key,
        base_url=base_url
    )

    tools = [who_are_you, what_can_you_do]
    # 通过bind_tools()将工具使用传入工具列表的方式,绑定到ChatOpenAI注册的llm中
    llm_with_tools = llm.bind_tools(tools)
    
	# 将函数转换为节点,接入链中
    message_formatter = RunnableLambda(format_messages)
    tool_executor = RunnableLambda(execute_tool_calls)
    
    processing_chain = message_formatter | llm_with_tools | tool_executor

    test_cases = [
        "你是谁?",
        "你的功能是什么?"
    ]
	# 通过enumerate获取索引和元素,test_case表示要遍历的列表,1 表示起始索引值
    # i对应起始索引值 1
    # question对应test_cases中的相应位置元素
    for i, question in enumerate(test_cases, 1):
        print(f"\n测试 {i}: {question}")
        print("-" * 60)
        try:
            result = processing_chain.invoke({"question": question})
            print(f"链式处理完成")
        except Exception as e:
            print(f"处理出错: {e}")

if __name__ == "__main__":
    main() 

说明:

@tool装饰器:在程序启动时,将此函数包装成一个标准的工具对象,这个对象包含了自动生成的JSON Schema。

在main函数中,这个工具对象被添加到tools列表中,并通过llm.bind_tools(tools)绑定到LLM

当用户问题被发送给LLM的时候,LLM会分析这个问题和可用的工具列表

LLM通过分析工具的描述和参数说明,决定调用这个工具

LLM根据参数名和上下文自动解析参数表示了什么

生成一个工具调用指令

execute_tool_calls函数接收到这个指令,找到工具对象,并调用其invoke方法。

invoke方法最终执行工具函数的实际代码,并按照工具的要求进行返回

格式化消息处理器

接收一个包含question字段的字典作为参数。

返回一个包含系统消息与人类消息的列表,其中人类消息是input中的question字段对应的值。

作用为进行角色设定,作为Runnable链的一部分,为后续的LLM处理做准备

执行工具调用

首先接收result (一个AIMessage对象),检查其中是否有result.tool_calls 。

如果存在的话则提取工具名,在tools列表中寻找那个被@tool装饰过的Tool对象

它调用工具函数,并将AI生成的参数传进去,从而执行了定义的Python函数

如果result.tool_calls不存在,则直接返回AI的普通聊天回复。

主函数的构建

进行初始化

定义工具列表

构造Runnable链

构造完善的处理链,将消息预处理、调用工具的LLM、工具执行全部加入链中

输出校验

MCP(Model Context Protocol,模型上下文协议)

本质:是一种标准化协议(由 Together AI 提出),定义了 “大模型与外部工具 / 系统交互” 的通用规范,核心是统一模型与工具之间的通信格式、上下文传递方式、错误处理规则等。

定位:属于协议层规范,类似于 HTTP 协议定义了网页通信的规则,MCP 定义了 “模型调用工具” 的通用规则。

作用:解决不同框架(如 LangChain、LlamaIndex)、不同模型、不同工具之间交互的 “兼容性问题”—— 比如按 MCP 规范开发的工具,能无缝对接任何支持 MCP 的大模型框架,无需单独适配。

简单理解:function call模块内部调用能力,mcp系统级,模块与模块之间,系统与系统之间的调用协议

示例:

用 MCPToolWrapper 包装你的 get_weather 函数,再通过 FastAPI 暴露成符合 MCP 协议的 HTTP 接口,让外部系统能访问。

# langchain_mcp_server.py
from fastapi import FastAPI, Request
from langchain_core.tools import tool
from langchain_mcp import MCPToolWrapper
from langchain_openai import ChatOpenAI
import json

# 1. 定义你的工具函数(和之前一样)
@tool
def get_weather(city: str) -> str:
    """获取指定城市的天气"""
    return f"{city}今天的天气是晴,温度20℃"

# 2. 用 MCPToolWrapper 包装工具(适配 MCP 协议)
mcp_wrapper = MCPToolWrapper(tools=[get_weather])
llm = ChatOpenAI(model="gpt-3.5-turbo")
# 构建 MCP 处理链:接收 MCP 请求 → 调用工具 → 返回 MCP 格式结果
mcp_chain = mcp_wrapper | llm

# 3. 用 FastAPI 搭建 HTTP 服务,暴露 MCP 接口
app = FastAPI()

@app.post("/mcp/invoke")
async def mcp_invoke(request: Request):
    """处理 MCP 协议的调用请求"""
    try:
        # 接收 MCP 格式的请求体(LlamaIndex 会按 MCP 标准发过来)
        request_data = await request.json()
        # 提取用户输入(MCP 协议的请求内容)
        user_input = request_data.get("input")
        
        # 用 MCP 链处理请求(自动按 MCP 格式调用工具)
        result = mcp_chain.invoke(user_input)
        
        # 按 MCP 协议返回结果
        return {
            "status": "success",
            "response": result.content,
            "mcp_version": "1.0"  # MCP 协议版本标识
        }
    except Exception as e:
        return {"status": "error", "message": str(e)}

# 启动服务(运行后会在 http://localhost:8000 提供 MCP 接口)
if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="0.0.0.0", port=8000)

服务会启动在 http://localhost:8000,对外暴露 /mcp/invoke 接口,这个接口完全遵循 MCP 协议。

通过异构服务:LlamaIndex 作为 MCP 客户端,通过 HTTP 调用上面的 MCP 服务,从而间接调用你的 get_weather 函数

# 更规范的 LlamaIndex MCP 客户端
from llama_index.mcp import MCPClient

# 初始化 MCP 客户端(指向 LangChain 的 MCP 服务)
client = MCPClient(
    server_url="http://localhost:8000/mcp/invoke",
    mcp_version="1.0"
)

# 调用 MCP 工具(LlamaIndex 自动按 MCP 协议发请求)
result = client.invoke("查一下广州的天气")
print("LlamaIndex 原生 MCP 调用结果:", result)
# 输出:LlamaIndex 原生 MCP 调用结果:广州今天的天气是晴,温度20℃

核心原理:MCP 是跨系统的通信协议,需把 LangChain 的 MCP 工具暴露为网络服务(而非进程内函数),LlamaIndex 作为 MCP 客户端通过网络调用;

关键步骤

LangChain 端:@tool 标记工具 → MCPToolWrapper 适配协议 → FastAPI 暴露 HTTP 接口;

LlamaIndex 端:用 MCP 客户端调用该 HTTP 接口,自动遵循 MCP 协议格式;

核心价值:你的 get_weather 函数本身还是 LangChain 内的方法,但通过 MCP 协议被封装成了 “标准化服务”,任何支持 MCP 的框架(LlamaIndex、甚至自定义系统)都能调用。

全部评论

相关推荐

ragflow v0.23.1 发布:Memory 稳定性全面增强,RAG 图像与表格理解升级,新增 GitHub / GitLab / Asana / IMAP 数据源1. 文档与说明改进• 修复文档中的错误说明• 在环境变量配置中新增默认密码的安全警告• 更新本地部署 LLM 的图示说明• 补充 Docker 构建中可选代理参数• 修正文档拼写错误• 新增 RAG 和 Agent 上下文引擎说明文档• 文档版本统一更新为 v0.23.1• 新增文档分类文件• 移除健康检查相关文档• 文档页面主题适配优化2. Memory 与时间一致性修复• 修复从 ES 初始化 Memory 大小时的问题• 重构时间戳一致性逻辑• 修复时间戳与 datetime 不一致的问题• 使用异步任务保存 Memory• 在删除 Memory 消息前判断索引是否存在• 修复 API Key 删除 Bug3. RAG 与解析能力增强• 修复 MDX 文件解析问题并正式支持• 修复数据集解析错误• 修复批量解析问题• 优化图像和表格上下文提取逻辑• 重构 metadata 提取规则以提升精度• 文档解析配置变更时,自动删除分块图片• 修复数值型 metadata 在 meta_filter 中导致的 TypeError4. Agent 与 Chat 相关修复与增强• 修复聊天页面存在错误信息时,后续消息引用显示异常的问题• 修复会话引用初始化错误,防止对话错位• 新建 Agent 的 begin 节点显示 undefined 的问题已修复• 支持在 Agent 页面和 Chat 页面中,仅选择使用相同 embedding 模型的知识库• 在 begin 节点增加显示模式设置• 修复消息选择删除逻辑问题5. 数据源与连接器• 新增 Asana 数据源接入及配置能力• 新增 IMAP 数据源接入、配置及同步能力• 新增 GitHub 数据源连接器• 新增 GitLab 数据源连接器• 修复 S3 数据源参数错误• 修复 S3 数据源页面样式问题6. 系统与管理后台优化• 管理后台用户列表支持按邮箱排序• 管理状态面板中不再显示未使用组件状态• 管理端分页在数据刷新后自动回到第一页的问题已修复7. 安全性与稳定性提升• 使用 ast.literal_eval 替换不安全的 eval 调用• 修复代码扫描安全告警中权限配置问题8. SDK 与 API 相关修复• 确保 rm_chunk API 中变量正确初始化• 移除 webhook 中输出 jsonschema 的代码• 修复应用知识库配置的 LLM 无法生效的问题9. 前端与 UI 修复• 修复动态翻译 key chunk.docType 显示异常• 修复 IDE 警告• 修复数据重新拉取后分页重置异常10. 架构与工程调整• 重构部分代码逻辑• 文档解析器在处理完成后正确关闭字节流• 更新模型提供方配置• 更新发布流程配置• 修复索引和数据删除流程中的边界问题
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务