手撸一个会“思考”的AI智能体

别再光看概念了,今天我们用Python和几行提示词,造一个能自己规划、调用工具、动态决策的智能体。

你是不是也听过这些词:Agent、智能体、自主决策...... 听起来很高级,但总觉得离实际代码有点远?

其实,抛开那些复杂的框架和炫酷的术语,一个最简智能体的核心逻辑,用一个while循环就能讲清楚。今天,我就带你从零实现一个旅行助手------它收到一句"查某城市天气,再推荐景点"这种模糊指令后,能自己琢磨出该先干什么、用什么工具、怎么根据结果决定下一步。

全程代码不超过100行,你复制粘贴就能跑起来(需要申请几个免费API Key)。

一、智能体的大脑:一个"思考-行动-观察"的死循环

在写代码之前,我们先理解一个最简单、也最核心的模型------ReAct 范式(Reason + Act)。

智能体不是一次性生成答案,而是不断重复这三个步骤:

  1. Thought(思考):根据当前情况,想一下下一步该做什么。
  2. Action(行动):执行一个具体操作,比如调用一个函数。
  3. Observation(观察):把操作的结果反馈回来,作为下一轮思考的依据。

就像你闭着眼睛走迷宫:摸一下(行动)→ 摸到墙了(观察)→ 我应该往左转(思考)→ 再迈一步(行动)... 直到走出去。

我们的旅行助手也是这样工作的。先上个最终效果图(实际运行日志):

vbnet 复制代码
用户: 查一下某城市今天的天气,然后根据天气推荐一个景点。

--- 第1轮 ---
Thought: 需要先获取某城市的实时天气。
Action: get_weather(city="某城市")
Observation: 某城市当前天气: Sunny,气温24°C

--- 第2轮 ---
Thought: 天气晴朗,适合户外,现在基于天气推荐景点。
Action: get_attraction(city="某城市", weather="Sunny")
Observation: 推荐某湖公园(划船)、某历史步道(徒步)

--- 第3轮 ---
Thought: 已经得到推荐,可以回答用户了。
Action: Finish[某城市今天晴朗温暖,推荐去某湖公园划船,或者沿历史步道散步。]

看到了吗?它没有一次性生成答案,而是自己决定分两步走,每一步都用到了上一步的真实结果。这才是"自主"的雏形。

下面我们就用代码把它造出来。

二、准备工作:三个工具 + 一个大脑

我们只需要三个东西:

  • 一个LLM (大脑):用来生成ThoughtAction。这里我用兼容OpenAI接口的模型(你可以用官方GPT、国产模型、或本地跑的Ollama)。
  • 两个简单的工具:天气查询API、景点推荐API(实际代码里我会用免费的wttr.in和一个通用的搜索API做示例)。
  • 一个解析器 :从LLM的输出里提取Action字段,然后调用对应的函数。

2.1 安装依赖

bash 复制代码
pip install requests openai tavily-python   # tavily是搜索API,你也可以换成别的

2.2 给LLM写"说明书"(System Prompt)

这是最关键的一步。我们要告诉LLM:你是谁、有哪些工具可用、输出格式必须严格遵循什么样子。

python 复制代码
AGENT_SYSTEM_PROMPT = """
你是一个智能旅行助手。你需要一步步完成用户请求。

# 可用工具:
- get_weather(city: str): 查询某城市的实时天气。
- get_attraction(city: str, weather: str): 根据城市和天气推荐景点。

# 输出格式(必须严格遵守):
每次回复只能包含一对 Thought 和 Action,格式如下:

Thought: 你的思考过程(为什么这样做、下一步计划)
Action: 具体动作,可以是:
   - 调用工具:get_weather(city="某城市")
   - 或者结束:Finish[最终答案]

# 重要规则:
- 一次只输出一对 Thought-Action,不要输出多对。
- Action 必须写在同一行。
- 当信息足够回答用户时,必须使用 Finish[答案] 结束。
"""

2.3 实现两个工具

工具1:查询真实天气(使用免费服务 wttr.in

python 复制代码
import requests

def get_weather(city: str) -> str:
    url = f"https://wttr.in/{city}?format=j1"
    try:
        resp = requests.get(url, timeout=5)
        resp.raise_for_status()
        data = resp.json()
        current = data['current_condition'][0]
        desc = current['weatherDesc'][0]['value']
        temp = current['temp_C']
        return f"{city}当前天气:{desc},气温{temp}°C"
    except Exception as e:
        return f"天气查询失败: {e}"

工具2:根据天气推荐景点(这里用Tavily搜索API,你也可以用百度/谷歌搜索API)

python 复制代码
from tavily import TavilyClient
import os

def get_attraction(city: str, weather: str) -> str:
    api_key = os.environ.get("TAVILY_API_KEY")
    if not api_key:
        return "错误: 未设置 TAVILY_API_KEY"
    client = TavilyClient(api_key=api_key)
    query = f"{city} {weather}天气下推荐的旅游景点"
    try:
        res = client.search(query, search_depth="basic", include_answer=True)
        # 优先使用生成的总结答案
        if res.get("answer"):
            return res["answer"]
        # 否则拼接前几条结果
        snippets = [r['content'] for r in res.get('results', [])[:3]]
        return "推荐:\n" + "\n".join(snippets) if snippets else "未找到相关景点"
    except Exception as e:
        return f"搜索失败: {e}"

把工具放进字典,方便后面调用:

python 复制代码
available_tools = {
    "get_weather": get_weather,
    "get_attraction": get_attraction,
}

2.4 LLM客户端(兼容OpenAI接口)

python 复制代码
from openai import OpenAI

class LLMClient:
    def __init__(self, model, api_key, base_url):
        self.model = model
        self.client = OpenAI(api_key=api_key, base_url=base_url)

    def generate(self, prompt, system_prompt):
        messages = [
            {"role": "system", "content": system_prompt},
            {"role": "user", "content": prompt}
        ]
        resp = self.client.chat.completions.create(
            model=self.model,
            messages=messages,
            temperature=0.3   # 低温度让输出更确定
        )
        return resp.choices[0].message.content

三、主循环:把一切串起来

核心代码不超过30行。关键步骤:

  1. 维护一个 prompt_history,把用户消息、每次的Thought-ActionObservation都拼进去。
  2. 循环调用LLM,每次得到输出后,用正则解析Action字段。
  3. 如果是Finish[...]就结束;否则解析出工具名和参数,从字典里调用对应函数,把结果包装成Observation: ...追加到历史里。

下面是完整的主循环代码(你可以直接复制粘贴,填上自己的API KEY):

python 复制代码
import re
import os

# 配置你的LLM(示例用某个兼容接口,请替换成真实的)
LLM_CONFIG = {
    "model": "gpt-3.5-turbo",  # 或你自己的模型名
    "api_key": "YOUR_API_KEY",
    "base_url": "https://api.openai.com/v1"   # 或其他兼容端点
}
os.environ["TAVILY_API_KEY"] = "YOUR_TAVILY_KEY"

llm = LLMClient(**LLM_CONFIG)

user_msg = "帮我查一下某城市今天的天气,然后根据天气推荐一个景点。"
history = [f"用户请求: {user_msg}"]
print(f"用户输入: {user_msg}\n")

MAX_LOOPS = 5
for step in range(MAX_LOOPS):
    print(f"=== 第 {step+1} 轮 ===")
    prompt = "\n".join(history)
    output = llm.generate(prompt, AGENT_SYSTEM_PROMPT)
    
    # 提取第一个完整的 Thought-Action 对(防止LLM抽风输出多对)
    match = re.search(r'(Thought:.*?Action:.*?)(?=\n\s*(?:Thought|Action|Observation)|\Z)', output, re.DOTALL)
    if match:
        output = match.group(1).strip()
    print(f"模型输出:\n{output}\n")
    history.append(output)
    
    # 解析 Action
    action_match = re.search(r"Action: (.*)", output)
    if not action_match:
        obs = "Observation: 错误:未找到Action,请按格式输出"
        print(obs)
        history.append(obs)
        continue
        
    action_str = action_match.group(1).strip()
    
    # 检查是否结束
    if action_str.startswith("Finish"):
        final = re.search(r"Finish\[(.*)\]", action_str).group(1)
        print(f"✅ 任务完成\n最终回答: {final}")
        break
    
    # 解析工具调用,例如 get_weather(city="某城市")
    tool_name = re.search(r"(\w+)\(", action_str).group(1)
    args_str = re.search(r"\((.*)\)", action_str).group(1)
    kwargs = dict(re.findall(r'(\w+)="([^"]*)"', args_str))
    
    if tool_name not in available_tools:
        obs = f"Observation: 错误:未知工具 {tool_name}"
    else:
        result = available_tools[tool_name](**kwargs)
        obs = f"Observation: {result}"
    
    print(obs + "\n")
    history.append(obs)

四、跑起来看看(真实运行日志)

我用上面的代码跑了一遍,下面是精简后的输出(已脱敏):

makefile 复制代码
用户输入: 帮我查一下某城市今天的天气,然后根据天气推荐一个景点。

=== 第 1 轮 ===
模型输出:
Thought: 需要先获取某城市当前的天气情况。
Action: get_weather(city="某城市")

Observation: 某城市当前天气:Sunny,气温24°C

=== 第 2 轮 ===
模型输出:
Thought: 天气晴朗,现在基于这个信息推荐一个适合户外活动的景点。
Action: get_attraction(city="某城市", weather="Sunny")

Observation: 推荐: 某湖公园(划船、野餐),某山徒步路线(风景优美)。

=== 第 3 轮 ===
模型输出:
Thought: 已经获得推荐,可以给出最终回答了。
Action: Finish[某城市今天晴朗温暖,很适合户外活动。推荐您去某湖公园划船,或者去某山徒步。]

✅ 任务完成
最终回答: 某城市今天晴朗温暖,很适合户外活动。推荐您去某湖公园划船,或者去某山徒步。

注意 :如果第一步查天气时API返回"多云",第二步的推荐会自动变成室内场所(比如博物馆、艺术馆)。这就是智能体和死板工作流的区别------没有if规则,完全由LLM根据观察动态推理。

五、你可以怎么玩?

这个最简版本已经具备了一个自主智能体的所有核心要素。你可以轻松扩展:

  • 加新工具 :比如"订酒店API"、"日历查询"、"发送邮件"。只要把函数放进available_tools,并在System Prompt里说明,LLM就会自主决定何时调用。
  • 换更强的模型:如果用GPT-4或Claude,规划能力和工具选择准确率会明显提升。
  • 增加记忆:把历史对话存入向量数据库,让智能体"记住"之前聊过的用户偏好。
  • 多智能体协作 :开两个这样的循环,一个负责查信息,一个负责写总结,让他们互相传递Observation

六、常见坑和解决

  • LLM输出格式飘忽 :正则解析容易失败。可以试试用function calling(OpenAI原生功能)替代文本解析,更稳定。
  • 陷入循环:比如反复查天气不结束。解决方案:在System Prompt里明确最大工具调用次数,或在代码里限制循环次数。
  • 工具参数错误 :LLM可能写出get_weather(city=北京)(漏了引号)。可以通过提示工程强调参数格式,或者在解析时做容错。

七、结语

现在你亲手写出一个能自主规划、调用工具、动态决策的智能体了。别被那些复杂的框架吓到------LangChain、CrewAI 等本质上都是在给这个简单的循环加糖衣。

当你看到LLM自己说出"我先查天气,再根据天气决定推荐什么"时,那种感觉会完全不同于调用一个API。它不再是机械地回答,而是在执行你交付的一个小型任务

下一步,你可以把这个框架应用到自己的实际场景中:自动收集竞品信息、写周报、做简单的数据分析...... 你会发现,80%的重复劳动,其实都可以用这种模式自动化。

相关推荐
会飞的蛛1 小时前
AI Coding 的终局,不是写更好的 Prompt,而是给 Agent 套上 Harness
ai编程
赛博三把手1 小时前
「2026 最新推荐」AI 大模型 API 中转站 | 国内直连 ChatGPT/Claude/Gemini 稳定优质的 API 接口服务
人工智能·github·ai编程
우리帅杰2 小时前
【AI测试】Python AI大模型介绍
开发语言·人工智能·python·ai编程
红信鸽3 小时前
Windsurf IDE实测:AI原生开发如何重构编程逻辑?
ai编程
Java知识技术分享3 小时前
node安装新版本,并解决opencode和claude code不能用问题
ai·个人开发·ai编程
放下华子我只抽RuiKe53 小时前
FastAPI 全栈后端(五):后台任务与消息队列
前端·javascript·react.js·ai·前端框架·fastapi·ai编程
四六的六3 小时前
Hybrid AI应用架构设计——WebView+LLM混合开发实践
人工智能·ai编程·webview·技术干货·llm大模型·端侧ai·hybrid ai
姚青&3 小时前
Rules(行为约束)
ai·ai编程