从0教你做一个AI编程智能体(一) · 智能体初识和搭建
结合最近的一些开发实践,聊聊当下很火的 AI Coding 领域。

现在大家或多或少都接触过AI编程工具,TRAE、Claude Code、Codex、Cursor……各类产品层出不穷。这篇文章要做的,是把这些编程智能体的底层逻辑拆开来看看——它们到底是怎么搭建的,开发思路又是如何一步步演进的。我们会从零开始,实现一个简单的编程智能体,文中也附了一些Claude Code的参考提示,方便快速上手。
整篇文章基于LangChain框架展开。
基础环境搭建
先安装必要的依赖包:langchain、langchain-openai、langchain-community、python-dotenv。
在项目根目录创建.env文件,配置环境变量:
API_KEY=
BASE_URL=
MODEL_NAME=
顺便说一句,uv是个比较新的Python包管理工具,可以理解为更快的pip+venv组合。不想折腾的话,直接用原生venv也完全没问题。
Key配置好之后,就可以开始开发Agent了。格式参考如下:
BASE_URL=https://api.deepseek.com/v1
MODEL_NAME=deepseek-chat
注意BASE_URL用的是/v1路径,走的是OpenAI兼容接口(/chat/completions)。
一个简单的智能体是怎么搭起来的
工具调用
说白了,智能体和普通LLM的区别,就在于多了一层环境感知的能力。给模型配上工具、加上动态提示词,一个最小可执行的Agent就成型了。
flowchart TD
A[用户输入] --> B[把消息历史和工具列表传给 LLM]
B --> C{LLM 判断是否需要调用工具}
C -- 不需要 --> D[直接生成回答]
C -- 需要 --> E[返回 tool_calls]
E --> F[程序根据工具名和参数执行函数/API]
F --> G[把执行结果包装成 ToolMessage]
G --> H[继续把结果传回 LLM]
H --> C
D --> I[返回给用户]
有了这个流程,我们就能做出一个天气播报员的例子——查一个天气查询API,把它封装成tool,提供给Agent使用,让Agent自己决定什么时候调用。
为了让Agent有上下文记忆,每次调用接口时需要把完整的聊天记录一并传入。同理,工具调用结果也需要回传。
LangChain的Message抽象类因此派生出了四种消息类型:HumanMessage、AIMessage、SystemMessage、ToolMessage。使用时,只需将内容包装成对应类型:
from langchain_core.messages import HumanMessage, AIMessage, SystemMessage, ToolMessage
messages = [
SystemMessage(content="你是一个天气播报员,专门为人们提供天气相关咨询"),
HumanMessage(content="给我查一下扬州这边的天气最近咋样"),
]
llm.invoke(messages)
定义一个工具也很直观,思路和写普通函数一样——AI调用工具时,同样是传参、拿结果:
from langchain.tools import tool
@tool
# 装饰器会解析函数签名,自动生成工具描述和参数格式,让 Agent 知道怎么使用这个工具
def get_weather(city: str) -> str:
"""查询指定城市的当前天气信息。参数 city 为城市名称,如'北京'。"""
# docstring 写清楚一些,AI 也会读
mock_data = {
"北京": "晴,25°C,微风",
"上海": "多云,28°C,东南风3级",
"广州": "小雨,30°C,湿度大"
}
return mock_data.get(city, f"未找到{city}的天气信息")
AI调用工具后会返回tool_calls(可能包含多个),结构大致如下:
[{
"name": "get_weather",
"args": {"city": "北京"},
"id": "call_xxx",
"type": "tool_call"
}]
理解了上面这些,结合流程图,参考相关API,就可以手动实现一个完整的Agent循环:
import os
import json
from dotenv import load_dotenv
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage, SystemMessage, ToolMessage
from langchain.tools import tool
load_dotenv()
# 1. 定义天气工具
@tool
def get_weather(city: str) -> str:
"""查询指定城市的当前天气信息。参数 city 为城市名称,如'北京'。"""
mock_data = {
"北京": "晴,25°C,微风",
"上海": "多云,28°C,东南风3级",
"广州": "小雨,30°C,湿度大"
}
return mock_data.get(city, f"未找到{city}的天气信息")
# 2. 绑定工具,让模型知道有哪些工具可用
tools = [get_weather]
llm = ChatOpenAI(
model=os.getenv("MODEL_NAME"),
api_key=os.getenv("API_KEY"),
base_url=os.getenv("BASE_URL"),
temperature=0
).bind_tools(tools)
tool_map = {t.name: t for t in tools}
# 3. 手动实现 Agent 核心循环
def run_manual_agent(user_input: str):
messages = [
SystemMessage(content="你是一个天气播报员,专门为人们提供天气相关咨询"),
HumanMessage(content=user_input)
]
print(f"? User: {user_input}")
while True:
# A: 让 LLM 决定下一步
response = llm.invoke(messages)
messages.append(response)
# B: 没有工具调用,说明已生成最终回复,退出循环
if not response.tool_calls:
print(f"? Assistant: {response.content}")
break
# C: 执行所有工具调用
for tool_call in response.tool_calls:
tool_name = tool_call["name"]
tool_args = tool_call["args"]
tool_id = tool_call["id"]
print(f"? Tool Call: {tool_name}({json.dumps(tool_args, ensure_ascii=False)})")
if tool_name in tool_map:
result = tool_map[tool_name].invoke(tool_args)
else:
result = f"错误:未知工具 '{tool_name}'"
print(f"? Tool Result: {result}")
messages.append(ToolMessage(content=str(result), tool_call_id=tool_id))
if __name__ == "__main__":
run_manual_agent("北京和上海今天天气怎么样?")
现在可以把模拟数据替换成真实接口了。LangChain也提供了封装好的create_agent(),可以省去手写循环的部分。用create_agent的版本精简如下:
import os
from dotenv import load_dotenv
from langchain.agents import create_agent
from langchain_openai import ChatOpenAI
from langchain.tools import tool
load_dotenv()
@tool
def get_weather(city: str) -> str:
"""查询指定城市的当前天气信息。
Args:
city: 城市名称,例如 "北京"、"上海"。
Returns:
包含该城市天气状况的字符串。
"""
mock_weather_data = {
"北京": "晴,气温 25°C,微风,空气质量优。",
"上海": "多云,气温 28°C,东南风 3 级。",
"广州": "小雨,气温 30°C,湿度较大。",
"深圳": "阴,气温 29°C,适合室内活动。"
}
return mock_weather_data.get(city, f"抱歉,暂时未找到 {city} 的天气信息。")
llm = ChatOpenAI(
model=os.getenv("MODEL_NAME"),
api_key=os.getenv("API_KEY"),
base_url=os.getenv("BASE_URL"),
temperature=0
)
agent = create_agent(
model=llm,
tools=[get_weather],
system_prompt="你是一个天气播报员,专门为人们提供天气相关咨询"
)
if __name__ == "__main__":
user_question = {"messages": [{"role": "user", "content": "北京今天天气怎么样?"}]}
result = agent.invoke(user_question)
print(result['messages'][-1].content)
Human in the Loop
LangChain生态中有一个经典示例——旅店入住系统。这个示例很好地诠释了「工具调用+人工介入」的基本范式。撇开代码细节,流程其实一目了然:
graph TD
A[客户入住说明] --> B{是否需要工具调用?}
B -- 否 --> C[直接回复客户]
B -- 是 --> D[调用系统 API]
D --> E{用户确认是否正确?}
E -- 确认 --> F[函数调用成功]
E -- 修改 --> G[输入修改后的信息]
G --> D
import os
from dotenv import load_dotenv
from langchain_openai import ChatOpenAI
from langgraph.prebuilt import create_react_agent
from langgraph.checkpoint.memory import InMemorySa ver
from langgraph.types import interrupt, Command
load_dotenv()
# 模拟订房工具:真正执行前先 interrupt,等待用户确认
def book_room(room_id: str) -> str:
"""预订指定房间。参数 room_id 为房间号,例如'301'。"""
human_response = interrupt({
"message": f"AI 准备调用 book_room(room_id={room_id}),是否确认执行?",
"room_id": room_id
})
if human_response["type"] == "accept":
final_room_id = room_id
elif human_response["type"] == "edit":
final_room_id = human_response["room_id"]
elif human_response["type"] == "reject":
return "用户拒绝了本次订房操作"
else:
return "未知的用户反馈"
return f"房间 {final_room_id} 已预订成功"
llm = ChatOpenAI(
model=os.getenv("MODEL_NAME"),
api_key=os.getenv("API_KEY"),
base_url=os.getenv("BASE_URL"),
temperature=0
)
checkpointer = InMemorySa ver()
agent = create_react_agent(
model=llm,
tools=[book_room],
checkpointer=checkpointer
)
config = {"configurable": {"thread_id": "room-demo"}}
# 第一次运行,程序会在 interrupt 处暂停
result = agent.invoke({"messages": [{"role": "user", "content": "帮我订一下301房间"}]}, config=config)
print(result)
# 模拟用户确认
result = agent.invoke(Command(resume={"type": "accept"}), config=config)
print(result)
# 模拟用户修改:Command(resume={"type": "edit", "room_id": "302"})
# 模拟用户拒绝:Command(resume={"type": "reject"})
到这里,是不是开始有点感觉了?做智能体其实没那么神秘,和传统业务开发的思路并无二致——原来写死的流程,现在交给模型来做路由判断;原来固定的分支,现在变成了工具调用。大模型的引入让程序有了「弹性」,但整体还是在划定的范式下运行。
下一篇会聊聊AI编程智能体的初步设计思路和实际编码过程——从Agent的设计范式,到工具的设计取舍,从结果出发、自顶向下地看待问题。这也是AI时代需要逐渐培养的思维方式。
文章如有错漏,欢迎在评论区批评指正,与大家共勉。