1. Tool-Calling 原理

我们借助 LangGraph 框架、基于 DeepSeek 模型的驱动、通过多个节点的协作,快速搭建起了一个智能编辑 Agent,并且帮我们生成了一篇完整的文章,整体流程还是比较清晰的。

但是,不知道大家有没有发现这样一个问题:Agent 实际生成文章的内容是比较陈旧的,有时可能甚至是一两年以前的知识,无法满足读者消费热点内容的需求。这是因为我们并没有开启联网搜索功能,还是依赖大模型的预训练数据,而由于预训练所要消耗的成本高昂,大模型通常要几个月才能训练一次,这就导致了我们文章的内容可能是过时的!想要解决这个问题也很简单——只需通过 Tool-Calling 功能,开发一个联网搜索的工具即可。

**Tool-Calling ,也叫做 Function-Calling,它是经典的扩展大模型能力边界的方式****,它的核心执行流程如下

  1. 在调用 LLM 生成内容时,额外传入一些工具或者函数的定义,每个工具都可以完成一项或多项特定的功能。
  2. 当大模型在生成回复时,如果推理出需要调用特定的工具,则会在消息中返回工具调用的名称即调用参数。
  3. 由业务应用解析调用参数,定位到需要调用的工具,实际执行调用。
  4. 业务应用将工具调用结果,作为下一条消息,继续发送给 LLM。
  5. LLM 结合上下文,以及工具调用结果,生成最终的完整回复。

img

需要特别注意的是:**LLM 的返回的只是需要调用的工具、以及工具调用的具体参数,而实际调用工具的行为还是需要业务应用自行来完成,无法靠 LLM 来代劳。这里的工具,可以是泛指广义上的扩展能力,它可以是一个本地函数、一个第三方 API、也可以是一个代码沙箱运行环境、或者一个数据库等等。****

2. 开发 Search Tool

理解了 Tool-Calling 的核心原理后,我们把它实际落地到我们的 Agent 中。在[现有的智能编辑 Agent 流程下],我们扩展一个新功能:*在撰写正文时,先根据文章的主题,到搜索引擎去检索相关的内容,并依据这些检索结果再去生成正文,这样可以大幅提升内容的时效性与权威性。*

要实现这个功能,首先我们需要开发一个网络搜索工具。这里我们直接采用 ****Serper API:****它是一个基于 Google Search 的简单易用的搜索 API,在 Google 的搜索能力上进行了低延迟和易用性的封装,可以基于关键词快速的完成网络检索功能。Serper API 的使用非常简单,大家自行查阅官方文档即可。

img

为了实现搜索功能,我们首先****将*****Serper API 封装成一个本地函数,基于 query 关键词检索相关结果。*

接下来,我们要把它封装成一个 LLM 可以理解的工具定义。在 LangGraph 下,定义一个工具非常简单,可以优先去内置的工具箱中查看是否有现成的工具,也可以基于 @tool 装饰器,直接将一个本地函数封装成一个 Tool。这里我们采用 @tool 装饰器的方式,函数的注释会被自动解析为工具和参数的描述信息。注意:*这里的 api_key 需要到 Serper API 官网创建。*

# 定义检索工具@tooldef search(query: str) -> str:    """根据关键词,在互联网上检索相关信息    Args:        query: 检索关键词    """        print(f"调用工具: [search], 调用参数: {query}")        # 构造请求参数    url = "https://google.serper.dev/search"    payload = json.dumps({        "q": query,    })    headers = {        "X-API-KEY": os.getenv("SERPER_API_KEY"),  # 在Serper官网申请api_key        'Content-Type': 'application/json'    }        # 发送请求    response = requests.request("POST", url, headers=headers, data=payload).json()        # 解析响应结果    if response['organic'][0]:        return response['organic'][0]['snippet']        return "没有搜索到相关结果"

这样,一个网络搜索工具就完成了!

3. Content Node 逻辑改造

Search Tool 实现完成之后,我们需要对现有的 Content Node 的逻辑进行改造。第一步就是要调整 System Prompt,引导 LLM 先去根据文章主题,去搜索引擎检索相关信息:

# 正文PromptCONTENT_PROMPT = """文章的标题为:《{title}》请先根据文章主题,在互联网上检索相关资料。接下来,根据资料的检索结果,按照总-分-总的结构,生成文章的主要段落。段落数在3~6段之间,可以结合你的经验和主题灵活调整。下面是一个可供参考的段落结构:1. xxx综述2. xxx的历史演进3. xxx的核心概念4. xxx实战5. xxx总结格式要求:生成的内容纯文本格式,每一段都要有一个二级标题(##)。段落之间保留2行空行。"""

第二步,我们定义一个工具列表,并将大模型与工具进行绑定。这一步可以直接通过 LangChain 内置的 bind_tools 函数来完成:

# 定义工具列表TOOLS = [search]TOOL_DICT = {tool.name: tool for tool in TOOLS}
# 将DeepSeek绑定工具列表deepseek = deepseek.bind_tools(TOOLS)

最后一步,调用 LLM 的流程也要进行修改:*我们需要先判断 LLM 的回复是否包含工具调用信息,如果包含,则需要解析工具调用参数,并执行工具调用,还有将工具调用结果封装成一条 ToolMessage , 继续传递给 LLM。*

最终的完整实现如下:

def content_node(state: ArticleState) -> ArticleState:    """    文章正文内容节点    :param state: 当前文章状态    :return: 处理后的文章状态    """        # 从当前状态中获取所需信息    if not state["topic"]:        raise ValueError("未指定文章主题!")    if not state["title"]:        raise ValueError("文章标题缺失!")    topic = state["topic"]    title = state["title"]        # 构造消息列表    messages = [        SystemMessage(content=SYSTEM_PROMPT.format(topic=topic)),        HumanMessage(content=CONTENT_PROMPT.format(title=title)),    ]        # 获取DeepSeek客户端    deepseek = get_deepseek()        # 将DeepSeek绑定工具列表    deepseek = deepseek.bind_tools(TOOLS)        # 调用DeepSeek,获取结果    while True:        # 调用LLM,并保存结果        reply = deepseek.invoke(messages)        messages.append(reply)                # 未返回工具调用参数,说明模型直接生成了回复,则更新状态后返回        if len(reply.tool_calls) == 0:            content = reply.content            state["content"] = content            print(f"文章正文生成完成:共 {len(content)} 字")            return state                # 解析工具调用参数        for tool_call in reply.tool_calls:            # 根据名称,获取需要调用的工具            tool_name = tool_call["name"]            tool = TOOL_DICT[tool_name]                        if tool is not None:                # 调用工具,获取结果                tool_call_result = tool.invoke(tool_call["args"])                print(f"工具调用结果: {tool_call_result}")                                # 保存工具调用结果                tool_msg = ToolMessage(content=tool_call_result, tool_call_id=tool_call["id"])                messages.append(tool_msg)                

4. Agent 节点编排

Content Node 改造好了之后,核心的功能就实现完成了,其它 Node 的功能保持不变。

**这里也可以充分体现出 LangGraph 的一大优势:将整个 Agent 的实现流程拆分到不同的 Node 中去完成,每个 Node 可以独立变更和扩展,不会对整体流程造成影响,这样就大幅提升了 Agent 系统的可扩展性和鲁棒性。****

最终编排 Agent 并执行的代码如下:

def build_agent() -> CompiledStateGraph:    """    构造Agent    :return: 编译好的Agent Graph    """        # 创建StateGraph图结构    sg = StateGraph(ArticleState)
    # 添加节点    sg.add_node("title_node", title_node)    sg.add_node("content_node", content_node)    sg.add_node("summary_node", summary_node)    sg.add_node("image_node", image_node)        # 添加边    sg.add_edge(START, "title_node")    sg.add_edge("title_node", "content_node")    sg.add_edge("content_node", "summary_node")    sg.add_edge("summary_node", "image_node")    sg.add_edge("image_node", END)        # 编译Graph并返回    graph = sg.compile()     return graph
def write_article(agent: CompiledStateGraph, topic: str) -> ArticleState:    """    撰写文章    :param agent: 内容编辑Agent    :param topic: 文章主题    :return: 最终生成的文章状态    """        # 设置初始状态    init_state = ArticleState(        topic=topic,        title="",        content="",        summary="",        image_path="",    )        # 执行Graph,返回生成的最终状态    return agent.invoke(init_state)
def dump_markdown(state: ArticleState) -> None:    """    将文章以Markdown格式保存到本地    :param state: 最终状态    :return: None    """        title = state["title"]    with open(f"./{title}.md", "w") as f:        # 写入标题        f.write(f"# {state["title"]}\n\n")        # 写入正文        f.write(f"{state["content"]}\n\n")        # 在末尾插入图片        f.write(f"![{title}]({state["image_path"]})\n\n")    print(f"文章已保存至:{title}.md")
def show_agent_structure(agent: CompiledStateGraph) -> None:    """展示Agent 结构"""        display(Image(agent.get_graph(xray=True).draw_mermaid_png()))        # 保存流程图到文件    graph_png = agent.get_graph(xray=True).draw_mermaid_png()    with open("agent_graph.png", "wb") as f:        f.write(graph_png)
if __name__ == '__main__':    # 构造Agent    agent = build_agent()        # 展示Agent结构图    # show_agent_structure(agent)        # 写文章    final_state = write_article(agent=agent, topic="中国苏超联赛持续火爆")        # 将文章导出为markdown格式    dump_markdown(final_state)    

这里我新增了一个函数 show_agent_structure:可以直观地打印出整个 Graph 的结构,展示结果如下:

img

5. 效果展示

我们指定一个近期的热点话题——**中国苏超联赛****,让编辑 Agent 围绕这个话题,去检索相关资料,并帮我们生成一篇文章。可以查看日志中的处理流程如下:

img

最终生成的文章内容为:

img

如何学习大模型 AI ?

由于新岗位的生产效率,要优于被取代岗位的生产效率,所以实际上整个社会的生产效率是提升的。

但是具体到个人,只能说是:

“最先掌握AI的人,将会比较晚掌握AI的人有竞争优势”。

这句话,放在计算机、互联网、移动互联网的开局时期,都是一样的道理。

我在一线互联网企业工作十余年里,指导过不少同行后辈。帮助很多人得到了学习和成长。

我意识到有很多经验和知识值得分享给大家,也可以通过我们的能力和经验解答大家在人工智能学习中的很多困惑,所以在工作繁忙的情况下还是坚持各种整理和分享。但苦于知识传播途径有限,很多互联网行业朋友无法获得正确的资料得到学习提升,故此将并将重要的AI大模型资料包括AI大模型入门学习思维导图、精品AI大模型学习书籍手册、视频教程、实战学习等录播视频免费分享出来。

在这里插入图片描述

第一阶段(10天):初阶应用

该阶段让大家对大模型 AI有一个最前沿的认识,对大模型 AI 的理解超过 95% 的人,可以在相关讨论时发表高级、不跟风、又接地气的见解,别人只会和 AI 聊天,而你能调教 AI,并能用代码将大模型和业务衔接。

  • 大模型 AI 能干什么?
  • 大模型是怎样获得「智能」的?
  • 用好 AI 的核心心法
  • 大模型应用业务架构
  • 大模型应用技术架构
  • 代码示例:向 GPT-3.5 灌入新知识
  • 提示工程的意义和核心思想
  • Prompt 典型构成
  • 指令调优方法论
  • 思维链和思维树
  • Prompt 攻击和防范

第二阶段(30天):高阶应用

该阶段我们正式进入大模型 AI 进阶实战学习,学会构造私有知识库,扩展 AI 的能力。快速开发一个完整的基于 agent 对话机器人。掌握功能最强的大模型开发框架,抓住最新的技术进展,适合 Python 和 JavaScript 程序员。

  • 为什么要做 RAG
  • 搭建一个简单的 ChatPDF
  • 检索的基础概念
  • 什么是向量表示(Embeddings)
  • 向量数据库与向量检索
  • 基于向量检索的 RAG
  • 搭建 RAG 系统的扩展知识
  • 混合检索与 RAG-Fusion 简介
  • 向量模型本地部署

第三阶段(30天):模型训练

恭喜你,如果学到这里,你基本可以找到一份大模型 AI相关的工作,自己也能训练 GPT 了!通过微调,训练自己的垂直大模型,能独立训练开源多模态大模型,掌握更多技术方案。

到此为止,大概2个月的时间。你已经成为了一名“AI小子”。那么你还想往下探索吗?

  • 为什么要做 RAG
  • 什么是模型
  • 什么是模型训练
  • 求解器 & 损失函数简介
  • 小实验2:手写一个简单的神经网络并训练它
  • 什么是训练/预训练/微调/轻量化微调
  • Transformer结构简介
  • 轻量化微调
  • 实验数据集的构建

第四阶段(20天):商业闭环

对全球大模型从性能、吞吐量、成本等方面有一定的认知,可以在云端和本地等多种环境下部署大模型,找到适合自己的项目/创业方向,做一名被 AI 武装的产品经理。

  • 硬件选型
  • 带你了解全球大模型
  • 使用国产大模型服务
  • 搭建 OpenAI 代理
  • 热身:基于阿里云 PAI 部署 Stable Diffusion
  • 在本地计算机运行大模型
  • 大模型的私有化部署
  • 基于 vLLM 部署大模型
  • 案例:如何优雅地在阿里云私有部署开源大模型
  • 部署一套开源 LLM 项目
  • 内容安全
  • 互联网信息服务算法备案

学习是一个过程,只要学习就会有挑战。天道酬勤,你越努力,就会成为越优秀的自己。

如果你能在15天内完成所有的任务,那你堪称天才。然而,如果你能完成 60-70% 的内容,你就已经开始具备成为一名大模型 AI 的正确特征了。

这份完整版的大模型 AI 学习资料已经上传CSDN,朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费

在这里插入图片描述

Logo

中国智能体开发者社区,聚焦智能体与大模型开发,提供前沿资讯、实用工具链、开源项目及行业案例。通过技术沙龙、开发者大赛等活动,促进经验交流与协作,助力开发者快速构建创新智能应用。

更多推荐