LangGraph实现工具调用Agent

假设现有的一些业务服务型平台中沉淀了很多零碎的接口,并且其中包含着未知但真实存在的上下文关系,如何让大模型 “自主” 构建复杂工作流,从而串联起更复杂的节点关系,但也要保证不失控,这是一个值得深入去做的、潜在能够实现显著性提效的一个思路。

Tool Calling与Function Calling的区别

区别在于 使用场景和封装方式

Function Calling

• 指常规的函数调用,也就是直接执行代码中定义的函数
示例:

复制

def add(a: int, b: int) -> int:
    return a + b
result = add(2, 3)
Tool Calling

• 特指特定框架(例如 LangChain)中对封装好功能的工具进行调用

• 工具通常包含额外元数据(如接口描述、参数校验等)、可组合成复杂工作流
示例:

复制

from langchain.tools import tool

@tool
def search_web(query: str) -> str:
    """通过搜索引擎查询信息"""
    return "搜索结果..."

result = search_web("Python教程")

除上文示例定义工具外,还可以通过继承BaseTool来写自定义工具。

复制

class SimpleCalculatorTool(BaseTool):
    name = "Simple Calculator"
    description = "执行基础数学运算"
    def _run(self, expression: str) -> str:
        return eval(expression)

两者的区别:

• @tool装饰器可以自动推断所属工具的名称和描述。

• 继承BaseTool的定义方式需要显式声明 name、description、_run方法,提供细粒度控制。

• 支持异步方法(_arun)

本小节将通过实现一个完整的工具调用Agent来展示LangGraph结合工具的强大能力。

工具调用Agent实现

这个Agent的主要能力:

• 识别用户输入意图

• 选择合适的工具

• 执行工具调用

• 生成最终响应结果

示意图:

图片

图片

定义状态与工具

复制

from typing import List, Dict, Optional
from pydantic import BaseModel
class Tool(BaseModel):
    name: str
    desc: str
    func: object

    class Config:
        arbitrary_types_allowed = True

class AgentState(BaseModel):
    messages: List[Dict[str, str]] = []
    current_input: str = ""
    thought: str = ""
    selected_tool: Optional[str] = None
    tool_input: str = ""
    tool_output: str = ""
    final_answer: str = ""
    status: str = "STARTING"
    error_count: int = 0
  • 1.
定义可用工具

复制

from custom_tools.simple_calculator_tool import SimpleCalculatorTool
from custom_tools.llm_doc_tool import create_medical_advisor_chain

tools = [
    Tool(
        name="simple_calculator",
        desc="用于执行基础数学运算,如加法、减法、乘法和除法。输入格式为:`2 + 2`,返回结果为 `4`。",
        func=SimpleCalculatorTool()._run,
    ),
    Tool(
        name="medical_advisor_chain",
        desc="用于提供医学建议。输入格式为:`请给我讲一个关于 {topic} 的医学建议`,返回结果为医学建议。",
        func=create_medical_advisor_chain()._run,
    ),
]
    实现核心节点

    复制

    # 意图识别节点
    async def think_node(state: AgentState) -> AgentState:
        prompt = f"""
          基于用户输入和当前对话历史,思考下一步行动。
            用户输入: {state.current_input}
            可用工具:{[t.name + ':' + t.desc for t in tools]}
          请决定:
          1.是否需要使用工具
          2.如果需要,选择哪个工具
          3.使用什么参数调用工具
    
          最终以JSON格式返回:{{"thought": "思考过程", "need_tool": true/false, "tool": "工具名", "tool_input": "参数"}}
        """
    
        llm = ChatTongyi(
            model_name="qwen2-72b-instruct",
            temperature=0,
            dashscope_api_key="",
        )
        response = await llm.ainvoke(prompt)
        content = response.content if hasattr(response, "content") else str(response)
        result = json.loads(content)
    
        # 更新状态
        return AgentState(
            **state.dict(exclude={"thought", "selected_tool", "tool_input", "status"}),
            thought=result["thought"],
            selected_tool=result.get("tool"),
            tool_input=result.get("tool_input"),
            status="NEED_TOOL" if result.get("need_tool") else "GENERATE_RESPONSE",
        )
    
    
    # 工具调用节点
    async def execute_tool(state: AgentState) -> AgentState:
        tool = next((t for t in tools if t.name == state.selected_tool), None)
        if not tool:
            return AgentState(
                **state.dict(),
                status="ERROR",
                final_answer="未找到指定工具",
            )
    
        try:
            print(f"调用工具: {tool.name}, 输入: {state.tool_input}")
            result = tool.func(state.tool_input)
            return AgentState(
                **state.dict(exclude={"tool_output", "status"}),
                tool_output=str(result),
                status="GENERATE_RESPONSE",
            )
        except Exception as e:
            print(f"工具调用异常: {e}")
            return AgentState(
                **state.dict(exclude={"final_answer", "status"}),
                status="ERROR",
                final_answer=f"工具调用失败: {str(e)}",
            )
    
    
    # 生成最终回答节点
    async def generate_response(state: AgentState) -> AgentState:
        prompt = f"""
            基于以下信息生成对用户的回复:
            用户输入: {state.current_input}
            思考过程: {state.thought}
            工具输出: {state.tool_output}
            
            请生成一个清晰、有帮助的回复给用户。
        """
    
        llm = ChatTongyi(
            model_name="qwen2-72b-instruct",
            temperature=0.7,
            dashscope_api_key="",
        )
    
        response = await llm.ainvoke(prompt)
        content = response.content if hasattr(response, "content") else str(response)
        return AgentState(
            **state.dict(exclude={"final_answer", "status"}),
            final_answer=content,
            status="SUCCESS",
        )
    构建工作流

    复制

    workflow = StateGraph(AgentState)
    
    workflow.add_node("think", think_node)
    workflow.add_node("execute_tool", execute_tool)
    workflow.add_node("generate_response", generate_response)
    
    # 定义路由函数
    def route_next_step(state: AgentState) -> str:
        if state.status == "ERROR":
            return "error"
        if state.status == "NEED_TOOL":
            return "execute_tool"
        elif state.status == "GENERATE_RESPONSE":
            return "generate_response"
        elif state.status == "SUCCESS":
            return END
        else:
            return "think"
    
    
    # 添加条件边
    workflow.add_conditional_edges(
        "think",
        route_next_step,
        {
            "execute_tool": "execute_tool",
            "generate_response": "generate_response",
            "error": "error_handler",
            END: END,
        },
    )
    
    workflow.add_conditional_edges(
        "execute_tool",
        route_next_step,
        {"generate_response": "generate_response", "error": "error_handler", END: END},
    )
    
    workflow.add_node("error_handler", error_handler)
    
    workflow.add_edge(START, "think")
    workflow.add_edge("generate_response", END)
    
    app = workflow.compile()
    • 1
    错误处理与重试机制

    复制

    async def error_handler(state: AgentState) -> AgentState:
        """处理错误情况"""
        if state.error_count < 3:
            return AgentState(
                **state.dict(exclude={"error_count", "status"}),
                error_count=state.error_count + 1,
                status="RETRY",
            )
        return AgentState(
            **state.dict(exclude={"final_answer", "status"}),
            final_answer="抱歉,我无法完成这个任务。",
            status="ERROR",
        )
    使用示例

    复制

    async def run_agent(user_input: str):
        state = AgentState(current_input=user_input)
        final_state = await app.ainvoke(state)
        # 兼容 dict 返回
        if isinstance(final_state, dict):
            return final_state.get("final_answer", "无结果")
        return getattr(final_state, "final_answer", "无结果")
    
    
    # 使用示例
    async def main():
        questions = ["计算23乘以45等于多少?", "高血压", "计算圆周率乘以10等于多少?"]
    
        for question in questions:
            print(f"\n问题: {question}")
            answer = await run_agent(question)
            print(f"回答: {answer}")
    
    
    # 运行示例
    import asyncio
    
    asyncio.run(main())
    执行结

    图片

    图片

    工具1-简单计算工具

    复制

    from langchain.tools import BaseTool
    from typing import Union
    
    
    class SimpleCalculatorTool(BaseTool):
        name: str = "simple_calculator"
        description: str = (
            "用于执行基础数学运算,如加法、减法、乘法和除法。输入格式为:`2 + 2`,返回结果为 `4`。"
        )
    
        def _run(self, operation: str) -> Union[float, str]:
            try:
                result = eval(operation)
                return result
            except Exception as e:
                return f"Error in calculation: {str(e)}"
    
        async def _arun(self, operation: str) -> Union[float, str]:
            return self._run(operation)
    • 1
    工具2-基于LCEL的医生助手

    复制

    from langchain_community.chat_models.tongyi import ChatTongyi
    from langchain_core.prompts import ChatPromptTemplate
    from langchain_core.output_parsers import StrOutputParser
    from langchain.tools import BaseTool
    
    
    class create_medical_advisor_chain(BaseTool):
        name: str = "medical_advisor_chain"
        description: str = (
            "用于提供医学建议。输入格式为:`请给我讲一个关于 {topic} 的医学建议`,返回结果为医学建议。"
        )
    
        def _run(self, topic: str) -> str:
            prompt_template = ChatPromptTemplate.from_messages(
                [
                    ("ai", "你是一个全世界最权威的医生"),
                    ("user", "请给我讲一个关于 {topic} 的医学建议"),
                ]
            )
    
            llm = ChatTongyi(
                model_name="qwen2-72b-instruct",
                temperature=0,
                dashscope_api_key="",
            )
    
            output_parser = StrOutputParser()
    
            chain = prompt_template | llm | output_parser
    
            return chain.invoke({"topic": topic})

    拓展思考

    • 假设现有的一些业务服务型平台中沉淀了很多零碎的接口,并且其中包含着未知但真实存在的上下文关系,如何让大模型 “自主” 构建复杂工作流,从而串联起更复杂的节点关系,但也要保证不失控,这是一个值得深入去做的、潜在能够实现显著性提效的一个思路。

    • 大模型应用的初衷与未来的落地一定在提效上,如果不能做到提效,它将没有存在的价值与意义。

    一、全套AGI大模型学习路线

    AI大模型时代的学习之旅:从基础到前沿,掌握人工智能的核心技能!

    因篇幅有限,仅展示部分资料,需要点击文章最下方名片即可前往获取

    二、640套AI大模型报告合集

    这套包含640份报告的合集,涵盖了AI大模型的理论研究、技术实现、行业应用等多个方面。无论您是科研人员、工程师,还是对AI大模型感兴趣的爱好者,这套报告合集都将为您提供宝贵的信息和启示。

    因篇幅有限,仅展示部分资料,需要点击文章最下方名片即可前往获

    三、AI大模型经典PDF籍

    随着人工智能技术的飞速发展,AI大模型已经成为了当今科技领域的一大热点。这些大型预训练模型,如GPT-3、BERT、XLNet等,以其强大的语言理解和生成能力,正在改变我们对人工智能的认识。 那以下这些PDF籍就是非常不错的学习资源。


    因篇幅有限,仅展示部分资料,需要点击文章最下方名片即可前往获

    四、AI大模型商业化落地方案

    因篇幅有限,仅展示部分资料,需要点击文章最下方名片即可前往获

    作为普通人,入局大模型时代需要持续学习和实践,不断提高自己的技能和认知水平,同时也需要有责任感和伦理意识,为人工智能的健康发展贡献力量

    评论
    成就一亿技术人!
    拼手气红包6.0元
    还能输入1000个字符
     
    红包 添加红包
    表情包 插入表情
     条评论被折叠 查看
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

    当前余额3.43前往充值 >
    需支付:10.00
    成就一亿技术人!
    领取后你会自动成为博主和红包主的粉丝 规则
    hope_wisdom
    发出的红包
    实付
    使用余额支付
    点击重新获取
    扫码支付
    钱包余额 0

    抵扣说明:

    1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
    2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

    余额充值