如何在 Web-Dev-For-Beginners 用 LangChain 工具调用让 AI 执行自定义函数
【免费下载链接】Web-Dev-For-Beginners24 Lessons, 12 Weeks, Get Started as a Web Developer项目地址: https://gitcode.com/GitHub_Trending/we/Web-Dev-For-Beginners
Web-Dev-For-Beginners 课程的 AI Framework 一课(10-ai-framework-project/README.md)演示了如何用 LangChain 的 tool calling 机制,让大模型在对话中识别并调用你自己定义的 Python 函数——比如让它执行一个加法函数add,或者调用外部 Web API 取回内容。读完并跑通本文后,你将拥有一个可以绑定自定义工具的 LangChain 客户端,并在模型判断需要某项能力时自动执行对应函数。
前提条件:
- Python 环境,通过 pip 安装依赖;
- 环境变量
GITHUB_TOKEN可用。课程文档说明:在 GitHub Codespaces 中该 token 已经配置好;如果在本地运行,需要自行创建一个具有相应权限的 personal access token; - 代码连接的是 GitHub Models(
base_url="https://models.github.ai/inference"),模型使用openai/gpt-4o-mini。
安装依赖并连接模型
课程给出的安装命令(见文档 Quick Start 与 Assignment 的 Step 1):
pip install langchain langchain-openai连接模型用ChatOpenAI客户端,这是后续所有工具调用代码的基础:
from langchain_openai import ChatOpenAI import os llm = ChatOpenAI( api_key=os.environ["GITHUB_TOKEN"], base_url="https://models.github.ai/inference", model="openai/gpt-4o-mini", )仓库中的 app.py 就是这个最小客户端;如果连llm.invoke("What is the capital of France")这一步都无法返回内容,说明 token 或网络配置有问题,应先解决它再继续工具调用部分。
定义自定义函数工具
LangChain 中一个“工具”由两部分组成:一个给模型看的参数蓝图(TypedDict 类),和一个真正执行的函数实现(functions字典)。课程文档用一个加法函数演示了完整写法:
from typing_extensions import Annotated, TypedDict class add(TypedDict): """Add two integers.""" # Annotations must have the type and can optionally include a default value and description (in that order). a: Annotated[int, ..., "First integer"] b: Annotated[int, ..., "Second integer"] tools = [add] functions = { "add": lambda a, b: a + b }各部分的作用(均出自课程文档的讲解):
- 类继承
TypedDict,类的 docstring(Add two integers.)成为工具的说明,模型据此判断“这个问题该不该用这个工具”; - 字段用
Annotated标注。文档中的注释明确了书写规则:annotation 必须包含类型,可选地按顺序附带默认值和描述; tools列表是传给模型的工具清单;functions字典是“工具箱”,键是工具名,值是真正执行的动作——模型只负责报出工具名和参数,执行发生在你的代码里。
绑定工具并触发调用
创建好工具后,用bind_tools把它挂到模型上:
llm_with_tools = llm.bind_tools(tools)bind_tools调用之后,llm_with_tools这个 LLM 实例就“知道”了这些工具的存在。接下来发送请求,并检查返回值中是否产生了tool_calls:
query = "What is 3 + 12?" res = llm_with_tools.invoke(query) if res.tool_calls: for tool in res.tool_calls: print("TOOL CALL: ", functions[tool["name"]](**tool["args"])) print("CONTENT: ", res.content)这里的执行逻辑是:模型分析 query 后,如果认定该用add工具,返回的res.tool_calls里就会有对应条目,每个条目的name指明要调哪个工具、args给出参数;functions[tool["name"]](**tool["args"])随即在你的进程里执行a + b。
把上面三段拼起来的完整脚本(与文档中的全量代码一致):
from langchain_core.messages import HumanMessage, SystemMessage from langchain_openai import ChatOpenAI import os from typing_extensions import Annotated, TypedDict class add(TypedDict): """Add two integers.""" a: Annotated[int, ..., "First integer"] b: Annotated[int, ..., "Second integer"] tools = [add] functions = { "add": lambda a, b: a + b } llm = ChatOpenAI( api_key=os.environ["GITHUB_TOKEN"], base_url="https://models.github.ai/inference", model="openai/gpt-4o-mini", ) llm_with_tools = llm.bind_tools(tools) query = "What is 3 + 12?" res = llm_with_tools.invoke(query) if res.tool_calls: for tool in res.tool_calls: print("TOOL CALL: ", functions[tool["name"]](**tool["args"])) print("CONTENT: ", res.content)验证结果
文档给出的示例输出为(注意是“output similar to”,实际措辞和空行可能不同):
TOOL CALL: 15 CONTENT:看到这个结果说明链路完整工作:模型从add工具的名字、描述和字段规格中识别出这是一次加法任务,functions字典里的 lambda 实际算出了 15。CONTENT:之后为空是文档示例中的正常现象——这一轮模型只报工具调用,没有另行生成文本。
如果res.tool_calls为空、只打印出CONTENT:,说明模型没有把这次请求路由到工具;文档没有给出更多排错顺序,此时可以先核对tools是否已传入bind_tools、query 是否确实匹配工具描述。
可选分支:让自定义函数调用外部 Web API
加法函数只演示机制,文档的进阶例子是让工具真正干活——joke工具会请求https://api.chucknorris.io/jokes/random?category={category}并返回 JSON 中的value字段:
class joke(TypedDict): """Tell a joke.""" category: Annotated[str, ..., "The joke category"] def get_joke(category: str) -> str: response = requests.get( f"https://api.chucknorris.io/jokes/random?category={category}", headers={"Accept": "application/json"} ) if response.status_code == 200: return response.json().get("value", f"Here's a {category} joke!") return f"Here's a {category} joke!" functions = { "add": lambda a, b: a + b, "joke": lambda category: get_joke(category) } tools = [add, joke]配套改动只有两处:把joke加进tools列表、把joke的实现加进functions字典,后面的bind_tools/invoke/tool_calls流程完全不变。查询换成:
query = "Tell me a joke about animals"文档给出的示例输出(同样是“something like”):
TOOL CALL: Chuck Norris once rode a nine foot grizzly bear through an automatic car wash, instead of taking a shower. CONTENT:两点需要注意:
- 这段代码
import requests,运行前环境中需要有requests库; get_joke在接口非 200 时会返回兜底文案Here's a {category} joke!,而不是抛异常。
仓库里可直接运行的完整脚本是 app-tools.py,内容与文档全量代码一致,add和joke两个工具都已绑定。
限制与下一步
- 工具是否被调用由模型根据工具名、描述和字段规格自行判断,文档中的说法是:模型“examined” query 后“made this determination from the tool's name, description, and field specifications”。描述写得模糊会增加误判概率,这一点由 TypedDict 的 docstring 和
Annotated描述字段控制。 res.tool_calls可能为空(模型直接作答),代码必须像文档示例那样先判断再执行,不能假设每次都会触发工具。
文档在“Building a complete AI application”一节把工具调用和会话记忆组合成了一个CodingAssistant类(含execute_tool分发逻辑),Assignment 则要求实现至少两个教育类工具的StudyAssistant,两者都在 10-ai-framework-project/README.md 内,可作为跑通本文后的延伸练习。
【免费下载链接】Web-Dev-For-Beginners24 Lessons, 12 Weeks, Get Started as a Web Developer项目地址: https://gitcode.com/GitHub_Trending/we/Web-Dev-For-Beginners
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考