上一篇把 Agent 的全景地图铺开了:模型层决策、编排层循环、工具层干活。地图看完了,这一篇开始铺路——三十分钟内,让一个真正会"自己决定调工具"的 Agent 在你机器上跑起来。
工具链选 2026 年 Python 社区的事实标准:uv 管环境,LangChain 1.x 的 init_chat_model 与 create_agent 做组装,LangSmith 看内部循环。全文按"装 uv → 建项目 → 管密钥 → 写 Agent → 拆原理 → 开追踪 → 避坑"的顺序展开,每条命令都可复制执行。
1、为什么是 uv
老一套 Python 环境管理要拼装好几个工具:pip 装包、venv 建环境、pyenv 管 Python 版本,依赖解析还慢。uv 把这三件事收进了一个用 Rust 写的单文件工具,附带锁文件与缓存,装依赖比 pip 快一个数量级。到 2026 年,它已是 Python 生态事实上的标准,FastAPI、LangChain 的官方文档都直接用它演示安装。
安装只需一条命令,按平台二选一。macOS、Linux 与 WSL 在终端执行:
curl -LsSf https://astral.sh/uv/install.sh | sh
脚本会把 uv 装到 ~/.local/bin 并配好 PATH。Windows 用户打开 PowerShell,执行:
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
装完重开一个终端,uv --version 能打印版本号就算成功。偏好包管理器的读者也可以 brew install uv、winget install astral-sh.uv 或 pip install uv,效果等价。细节见 uv 官方文档。
2、初始化项目与依赖
找个工作目录,三条命令完成建项目、进目录、装依赖:
uv init hello-agent
cd hello-agent
uv add langchain langgraph langchain-openai python-dotenv
uv init 生成项目骨架,uv add 装包并写入 pyproject.toml,同时创建虚拟环境 .venv 与锁文件 uv.lock。四个包各司其职:
langchain:主包,提供create_agent与init_chat_modellanggraph:图编排引擎,Agent 循环的底座langchain-openai:OpenAI 接入层,1.x 主包不捆绑供应商包,用谁装谁python-dotenv:从.env文件加载环境变量
执行完 uv add,目录长这样:
hello-agent/
├── .python-version # 项目锁定的 Python 版本
├── .venv/ # uv 自动创建的虚拟环境
├── main.py # 默认入口脚本,后面会替换
├── pyproject.toml # 依赖声明
├── README.md
└── uv.lock # 精确到版本的锁文件
关键是 uv.lock:它钉死每个依赖的精确版本,任何人拿到项目执行 uv sync 就能复现一模一样的环境,记得把它提交进 git。本系列基线是 Python 3.11+,本机版本不够时 uv 会自动下载托管版本,也可以用 uv python pin 3.12 显式钉住。
3、API Key 管理
调模型需要 API Key。OpenAI 的 Key 在 OpenAI 平台的 API Keys 页创建,以 sk- 开头。密钥管理只有一条铁律:绝不写进代码。 代码会截图、会分享、会提交 git,每一种途径都等于把 Key 白送出去。
本地开发的标准做法:密钥放项目根目录的 .env 文件,程序启动时用 python-dotenv 加载。文件内容如下:
# OpenAI API Key,在 https://platform.openai.com 创建
OPENAI_API_KEY=sk-你的真实密钥
读取只要两行:from dotenv import load_dotenv 导入,load_dotenv() 调用,之后 langchain-openai 会自动从环境变量 OPENAI_API_KEY 取值,代码里不出现任何密钥字样。格式约定见 python-dotenv 仓库。
提醒:检查
uv init生成的.gitignore是否包含.env,没有就补一行,防止密钥随代码提交。
4、第一个 Agent
目标:问 Agent 现在几点了,它不靠模型猜,而是自己决定调用一个真实的 Python 函数取时间。完整代码不到 30 行,新建 agent.py(uv init 生成的 main.py 可以删掉),分两段看。
4.1、定义工具
第一段是导入与工具定义:
import datetime
from dotenv import load_dotenv
from langchain.agents import create_agent
from langchain.chat_models import init_chat_model
from langchain_core.tools import tool
load_dotenv() # 把 .env 中的键值对注入环境变量
@tool
def get_current_time() -> str:
"""获取当前本机日期和时间,格式为 YYYY-MM-DD HH:MM:SS。"""
return datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
@tool 装饰器把普通函数变成 Agent 工具:函数名是工具名,类型注解生成参数 schema,docstring 原样发给模型当说明书。模型看不到函数体,只凭这段描述判断何时调用,所以 docstring 要把"能干什么"写清楚——这是工具设计的第一课。
4.2、创建模型并组装
第二段建模型、组装 Agent、发起调用:
model = init_chat_model("openai:gpt-5")
agent = create_agent(
model,
tools=[get_current_time],
system_prompt="你是严谨的助手,凡涉及当前时间的问题,必须先调用工具查询。",
)
result = agent.invoke(
{"messages": [{"role": "user", "content": "现在几点了?请精确到秒。"}]}
)
print(result["messages"][-1].content)
init_chat_model 是 LangChain 1.x 的统一模型入口:一个 供应商:模型名 字符串抹平各家差异,其后参数可透传 timeout、max_retries 等供应商选项。想换 Claude,装上 langchain-anthropic,字符串改成 anthropic:claude-sonnet-5,其余代码一行不动(Key 在 Anthropic 控制台创建,环境变量名 ANTHROPIC_API_KEY)。签名细节见 init_chat_model 参考。
create_agent 返回编译好的 LangGraph 图。invoke 进去一个消息列表,出来的是完整对话状态,result["messages"][-1] 就是最终回答。
老教程里常见的 create_react_agent 来自 langgraph.prebuilt,1.0 后已被官方标记弃用,推荐换用 create_agent。两者参数几乎一致,只是 prompt 改名 system_prompt,旧写法长这样:
from langgraph.prebuilt import create_react_agent
agent = create_react_agent(
model,
tools=[get_current_time],
prompt="你是严谨的助手,涉及当前时间必须先调用工具查询。",
)
这段代码今天仍能运行,只是会收到弃用警告;新项目直接用 create_agent,省得日后迁移。两份参考:create_agent、create_react_agent。
4.3、运行
存盘后执行:
uv run agent.py
uv run 自动使用项目 .venv 里的解释器。几秒后终端输出:
现在是 2026-10-23 21:07:44,精确到秒。
措辞每次可能不同,这不重要;重要的是这个时间戳来自你机器上真实的函数调用,不是模型编的。怎么证明?先看原理,再看追踪。
5、一次调用发生了什么
拆开刚才那次 invoke,内部循环一共五步:
- 输入被包装成
HumanMessage,进入 Agent 的消息列表 - 模型节点带着 system prompt 与工具 schema 调用 GPT-5
- 模型返回带
tool_calls的AIMessage:指明调哪个工具、传什么参数 - 工具节点执行你的 Python 函数,结果包成
ToolMessage回填列表 - 模型读到工具结果,生成最终回答;不再有
tool_calls,循环终止
第二步值得多看一眼:模型拿到的除了对话,还有 @tool 自动生成的 JSON schema——工具名、参数、描述。第三步是关键分界:模型只下指令,不执行任何代码,tool_calls 里就是一段结构化的"我想调用 get_current_time"。
工具跑完,第四步把结果作为新消息追加,第五步模型带着"观察"重新推理,组织出自然语言答案。这个"推理 → 行动 → 观察 → 再推理"的循环就是 ReAct 范式,全景篇的概念在这里落了地:
模型从头到尾没有执行过代码——它只负责决策,执行永远发生在你的 Python 进程里。
记住这个边界,后面写多工具、多轮循环时你才知道:工具是你写的,权限是你给的,模型只用嘴指挥。
6、开通 LangSmith 追踪
Agent 是黑盒循环,print 很快不够用。LangSmith 是官方追踪平台,接入零代码:注册 smith.langchain.com(免费档够学习用),在 Settings → API Keys 创建 lsv2_ 开头的密钥,往 .env 追加两行:
# LangSmith 追踪开关与密钥
LANGSMITH_TRACING=true
LANGSMITH_API_KEY=lsv2_pt_你的密钥
langchain-core 检测到这两个变量就会自动上报,不用改一行代码。重跑 uv run agent.py,打开 LangSmith 的 Projects 页面(默认进 default 项目),点开最新 trace:三层嵌套一目了然——最外层是 Agent 图,中间是两次模型调用,里面挂着 get_current_time 的执行记录,每层的输入输出、token 消耗、耗时全部可视。
调试时重点看两处:模型第一次返回的 tool_calls(它决定调什么、传什么参数),以及 ToolMessage(工具真实返回了什么)。旧教程里的 LANGCHAIN_TRACING_V2 是历史变量名,现在统一用 LANGSMITH_ 前缀。建议从今天起常开追踪,本系列后续的调试全靠它。
7、常见坑
第一坑,Python 版本。本系列基线 Python 3.11+,老系统自带的 3.8、3.10 先升级。uv 顺手就能办:uv python install 3.12 下载托管版本,uv python pin 3.12 在项目里钉住,全程不碰系统 Python。
第二坑,网络。国内直连 OpenAI、Anthropic 经常超时,报 Connection error 或长时间无响应先查这条。两个办法:给终端配代理,如 HTTPS_PROXY=http://127.0.0.1:7890(端口换成自己的);或用 init_chat_model("openai:gpt-5", base_url="https://中转地址/v1") 走兼容网关。
第三坑,计费。Agent 循环放大消耗:每轮工具回传都把全量历史重发给模型,一个失控循环几美元就没了。开发期在 OpenAI 平台设好用量告警与月度上限,调试用 openai:gpt-5-mini 这类便宜模型,上线前再换。
第四坑,版本冲突。典型原因是全局 pip 装过一套、uv 又装一套,解释器对不上。原则:进了 uv 项目就全用 uv,不碰 pip;团队协作提交 uv.lock;升级统一 uv lock --upgrade 后 uv sync,别手改 pyproject.toml 里的版本号。
8、总结
这篇从零跑通了第一个 Agent:uv 建环境、.env 管密钥、@tool 定义工具、init_chat_model 统一模型入口、create_agent 组装循环、LangSmith 看清内部五步。你手里现在有一个可复制的最小 Agent 骨架,本系列后面所有内容都在它上面生长。
留两个练习:一是给 get_current_time 加 timezone: str 参数,让模型能查任意时区的时间(提示:参数写进函数签名,schema 自动更新);二是加第二个工具,比如四则运算计算器,观察模型如何在两个工具间做选择,再去 LangSmith 对比每次的 tool_calls 决策。
下一篇讲 LLM 调用基础:消息类型、流式输出、多模型切换,把 invoke 背后的细节补全。对于刚起步的读者,建议今天就照着敲一遍——环境这关过了,后面的路全是平的。


