从0教你做一个AI编程智能体(一) · 智能体初识和搭建

大家好,我是小奶龙,作为平台新人,想结合最近的一些开发经验,和大家聊聊现在很火的 AI Coding 领域

这个时代,大家或多或少都用过一些 AI 编程工具,包括但不限于 TRAE、Claude Code、Codex、Cursor 之类。本文将带你一步步拆解:这些编程智能体到底是怎么搭出来的,开发思路又是怎么演进的。我们会从零实现一个简单的编程智能体,文中也附了一些 Claude Code 的参考提示,方便你快速搭建,跟上教程节奏。

模型别选太差的,deepSeek-v4-pro 即可,至少要能理解 LangChain 的基本概念。

本文基于 LangChain 框架展开讲解


基础环境搭建

安装以下依赖包:langchainlangchain-openailangchain-communitypython-dotenv

同时在项目根目录创建 .env 文件,配置你的环境变量:

ini 复制代码
API_KEY=
BASE_URL=
MODEL_NAME=

!NOTE 如果需要将代码上传到 GitHub,请务必在 .gitignore 中添加 .env,避免 Key 泄露。把敏感信息从代码中剥离,也是工程开发的基本规范。
!TIP 使用 Claude Code 快速搭建 prompt

使用 uv 创建一个虚拟环境,安装 langchainlangchain-openailangchain-communitypython-dotenv,然后创建 .env 文件(并将其加入 .gitignore),写入以下环境变量,空值留我来填写:

ini 复制代码
API_KEY=
BASE_URL=
MODEL_NAME=

注:uv 是一个较新的 Python 包管理工具,可以简单理解为更快的 pip + venv,不想折腾的话直接用原生 venv 也完全没问题。

配置好 Key 之后,就可以开始开发你的 Agent 了。格式参考如下:

ini 复制代码
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 有上下文记忆,每次调用接口时需要把完整的聊天记录一并传入。同理,为了让 Agent 能通过工具感知外部信息,工具的调用结果也需要回传。

LangChain 的 Message 抽象类因此派生出了四种消息类型:HumanMessageAIMessageSystemMessageToolMessage。使用时,只需将内容包装成对应类型即可:

arduino 复制代码
from langchain_core.messages import HumanMessage, AIMessage, SystemMessage, ToolMessage

messages = [
    SystemMessage(content="你是一个天气播报员,专门为人们提供天气相关咨询"),
    HumanMessage(content="给我查一下扬州这边的天气最近咋样"),
]

llm.invoke(messages)

定义一个工具也很直观,思路和写普通函数一样、AI 调用工具时,同样是传参、拿结果:

python 复制代码
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(可能包含多个),结构大致如下:

css 复制代码
[    {        "name": "get_weather",        "args": {"city": "北京"},        "id": "call_xxx",        "type": "tool_call"    }]

相关文档参考:

⚠️ 注意:ToolMessage 里的 tool_call_id 必须和模型返回的 tool_calls 里的 id 严格对应,因为模型可能一次性返回多个工具调用,框架需要知道哪条结果对应哪次调用。

理解了上面这些,结合流程图,参考相关 API,就可以手动实现一个完整的 Agent 循环了:

python 复制代码
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("北京和上海今天天气怎么样?")

现在可以把模拟数据替换成真实接口了,工作流参考如下:

先搜索"免费天气 API",找到可用的接口地址,直接把 URL 给 Claude Code。

推荐 Open-Meteo,免费且不需要 Key:open-meteo.com/en/docs

参考提示词 :根据 https://open-meteo.com/en/docs,把天气查询的模拟数据替换为真实接口调用。保留 get_weather(city: str) 这个 tool 的函数签名,如果接口需要经纬度,在函数内部维护一个城市到经纬度的简单映射,覆盖北京、上海、广州、深圳即可。


LangChain 也提供了封装好的 create_agent(),可以省去手写循环的部分,具体 API 参考:

create_agent 的版本精简如下:

python 复制代码
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
py 复制代码
import os
from dotenv import load_dotenv

from langchain_openai import ChatOpenAI
from langgraph.prebuilt import create_react_agent
from langgraph.checkpoint.memory import InMemorySaver
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 = InMemorySaver()

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 时代我们需要逐渐培养的思维方式。

文章如有错漏,欢迎在评论区批评指正,与大家共勉!


最后,自荐一下我的开源 Coding Agent 项目 SuperCodegithub.com/zongxi1115/...

定位上,SuperCode 以 GUI 为核心:相比 Codex,对第三方模型的兼容性更好;相比 Claude Code,没有繁琐的配置,一路安装即可上手,对新手友好。

功能上,我们把一些常见的开发范式融了进来,比如项目看板、项目知识库体系------后者相比目前的 AGENTS.md 方案,能让 AI 对项目背景的理解更清晰。部署层面也做了一些优化,通过 Session 机制避免传入敏感的账户密码,降低信息泄露风险。

目前项目还处于起步阶段,也有不少新想法在推进:AI 输出期间能做哪些事、如何让生成的代码对开发者更可控、Claude Artifacts 思路的实践尝试......正在一步步落地。

如果感兴趣,欢迎试用,点个 Star、提个 Issue 或 PR,就是对我最大的支持!

相关推荐
罗小罗同学1 小时前
Nat Med发表SPARK智能体框架,可以自主思考、提出假设、设计实验并验证结果,让AI也能主动发现肿瘤生物学规律
大数据·人工智能·spark·医学图像处理
团象科技1 小时前
跨境服务与产品多地域迭代场景下 生成式AI安全部署的实操路径观察
服务器·人工智能
YOLO数据集集合1 小时前
无人机航拍人体检测数据集|低空巡检搜救智能监控|YOLO目标检测算法训练集
人工智能·深度学习·yolo·目标检测·无人机
逻辑君1 小时前
Foresight研究报告【20260013】
人工智能·机器学习
明月照山海-1 小时前
机器学习周报四十七
人工智能·机器学习
weixin_468466851 小时前
图像处理之亚像素边缘检测新手教程
图像处理·人工智能·自动化·图像分割·机器视觉·亚像素·光学系统
落叶无情1 小时前
第二章 ICEF核心知识解读 第三节 ICEF对AI推理能力的系统性增强:机制、效果与深层价值
人工智能
AwakeFantasy1 小时前
聊聊一些关于信息收集的能力
人工智能·经验分享
YueJoy.AI1 小时前
AI应用的质量保障:从测试到监控的完整流程
人工智能·ai·语言模型