• 欢迎访问少将全栈,学会感恩,乐于付出,珍惜缘份,成就彼此、推荐使用最新版火狐浏览器和Chrome浏览器访问本网站。
  • 吐槽,投稿,删稿,交个朋友
  • 如果您觉得本站非常有看点,那么赶紧使用Ctrl+D 收藏少将全栈吧

AI Agent 开发入门:用 Python 从零搭建一个能联网搜索的 AI Agent

AI Coding 实测 admin 5小时前 11次浏览 已收录 扫描二维码

“Agent” 这个词这两年被用烂了。各种框架都在推 Agent 概念,但你仔细看,大部分所谓的 Agent 就是一个 LLM 调用包装了一层 if-else。

真正有用的 Agent 其实没那么复杂——给 LLM 一组工具函数,让它自己决定什么时候调用、用什么参数,然后把结果喂回去继续推理。

这篇用 Python 从零搭一个能联网搜索的 AI Agent,不依赖任何 Agent 框架,代码加起来不到 150 行。

Agent 的本质是什么

去掉所有营销包装,一个 Agent 的核心就三件事:

  1. LLM 决定调用哪个工具 — 根据用户问题,输出结构化的函数调用请求
  2. 执行工具函数 — 实际调用外部 API 或执行代码,拿到结果
  3. LLM 基于结果继续推理 — 把工具返回的信息整合进上下文,生成最终回答

这个循环可以重复多次。比如用户问”2026 年最好的 AI 编程工具有哪些”,Agent 可以先搜一下最新评测,再比较几家定价,最后给出综合建议。

OpenAI 在 2023 年 6 月首次推出了 Function Calling API(官方文档:https://platform.openai.com/docs/guides/function-calling),这个概念迅速成为 AI Agent 的标准范式。到 2026 年,几乎所有主流 LLM 都支持类似的能力。

第一步:定义工具函数

先定义一个搜索引擎工具。为了演示,我们用 DuckDuckGo 的免费 API(不需要 API Key),以及一个计算器工具。

import json

import requests

def search_web(query: str) -> str:

"""搜索网络获取最新信息"""

try:

url = f"https://api.duckduckgo.com/?q={query}&format=json&no_html=1"

resp = requests.get(url, timeout=10)

data = resp.json()

# 提取摘要和相关话题

results = []

if data.get("AbstractText"):

results.append(data["AbstractText"])

for topic in data.get("RelatedTopics", [])[:3]:

if isinstance(topic, dict) and "Text" in topic:

results.append(topic["Text"])

return "\n".join(results[:5]) if results else "未找到相关信息"

except Exception as e:

return f"搜索出错: {str(e)}"

def calculate(expression: str) -> str:

"""执行数学计算"""

try:

# 安全计算,只允许基本数学运算

allowed = set("0123456789+-*/.() ")

if not all(c in allowed for c in expression):

return "不支持的表达式"

result = eval(expression, {"__builtins__": {}}, {})

return str(result)

except Exception as e:

return f"计算错误: {str(e)}"

注意 search_web 使用 DuckDuckGo 的免费 API。对于生产环境,建议换成 SerpAPI 或 Bing Search API——免费额度有限但更稳定。

第二步:定义工具 Schema

Function Calling 的关键在于告诉 LLM 每个工具的参数结构。OpenAI 的 API 用 JSON Schema 来描述:

tools = [

{

"type": "function",

"function": {

"name": "search_web",

"description": "搜索网络获取最新信息,当需要最新数据、新闻或不确定的信息时使用",

"parameters": {

"type": "object",

"properties": {

"query": {

"type": "string",

"description": "搜索关键词,尽量精准"

}

},

"required": ["query"]

}

}

},

{

"type": "function",

"function": {

"name": "calculate",

"description": "执行数学计算,支持加减乘除和括号",

"parameters": {

"type": "object",

"properties": {

"expression": {

"type": "string",

"description": "数学表达式,如 '15000 * 0.15'"

}

},

"required": ["expression"]

}

}

}

]

这个 schema 的质量直接影响 Agent 的准确度。description 字段不是摆设——LLM 靠它来判断什么时候调用哪个工具。写清楚工具的适用场景,能减少大量误调用。

第三步:实现 Agent 循环

核心逻辑:发消息给 LLM → 如果返回 tool_calls 就执行工具 → 把结果送回 LLM → 直到 LLM 直接回复用户。

from openai import OpenAI

client = OpenAI(api_key="你的API密钥")

def run_agent(user_message: str, max_turns: int = 5) -> str:

messages = [

{"role": "system", "content": "你是一个智能助手,可以使用搜索和计算工具来回答用户问题。如果需要最新信息,优先使用搜索工具。"},

{"role": "user", "content": user_message}

]

for turn in range(max_turns):

response = client.chat.completions.create(

model="gpt-4o-mini",

messages=messages,

tools=tools,

tool_choice="auto"

)

message = response.choices[0].message

# 如果没有 tool_calls,说明 LLM 准备直接回答

if not message.tool_calls:

return message.content

# 执行工具调用

messages.append(message)

for tool_call in message.tool_calls:

function_name = tool_call.function.name

arguments = json.loads(tool_call.function.arguments)

if function_name == "search_web":

result = search_web(arguments["query"])

elif function_name == "calculate":

result = calculate(arguments["expression"])

else:

result = f"未知工具: {function_name}"

messages.append({

"tool_call_id": tool_call.id,

"role": "tool",

"name": function_name,

"content": result

})

return "Agent 达到最大轮次限制,未能完成回答"

上面代码有几处故意留的拼写错误。完整的正确版本见文末。

第四步:跑起来试试

if __name__ == "__main__":

# 测试1:需要搜索的问题

result = run_agent("2026年 iPhone 18 Pro 的主要升级是什么?")

print(result)

# 测试2:需要计算的问题

result = run_agent("如果订阅 API 每月 100 万次调用,每次 $0.002,月费多少?")

print(result)

当用户问 iPhone 18 Pro 时,Agent 会调用 search_web 搜索最新信息。当问计算时,它会调 calculate。整个过程对用户透明。

实际运行中的几个坑

跑了两个月 Agent 之后,遇到的几个常见问题:

1. LLM 过度依赖工具

有时候 LLM 会为了一个它明明知道答案的问题也去搜一下。比如”Python 的列表推导式怎么用”,它可能也去搜一下。解决方法是优化 system prompt,明确告诉它”只有需要最新信息时才搜索”。

2. 工具返回结果太长

搜索结果可能几千字。LLM 的上下文窗口虽然越来越大,但 token 消耗也大。可以加一个 max_results 参数控制返回长度,或者在工具函数内部做截断。

3. 循环调用

偶尔 LLM 会陷入死循环——调一个工具 → 不满意 → 再调同一个工具。解决办法是 max_turns 限制,同时在 system prompt 里加一句”如果工具返回的结果不足以回答,直接告诉用户你不知道”。

根据 OpenAI 官方文档,Function Calling 每次调用会额外消耗大约 100-200 tokens 来描述工具和参数。如果你定义了 5 个复杂的工具,每次请求的 token 消耗会增加约 1000 tokens。对于生产环境,这个开销需要考虑。

进阶:让 Agent 更可靠

加入记忆

上面的 Agent 每次都是”一次性”的。要让它记住对话历史,把 messages 存起来:

class PersistentAgent:

def __init__(self):

self.messages = [

{"role": "system", "content": "..."}

]

def chat(self, user_input: str) -> str:

self.messages.append({"role": "user", "content": user_input})

# ... 同上循环逻辑 ...

self.messages.append({"role": "assistant", "content": result})

return result

错误重试

网络请求可能失败,API 可能超时。给工具调用加重试逻辑:

def call_with_retry(func, args, max_retries=2):

for i in range(max_retries):

try:

return func(**args)

except Exception as e:

if i == max_retries - 1:

return f"错误: {str(e)}"

time.sleep(1)

完整代码

以上所有代码的完整可运行版本(修正了所有拼写错误)见 GitHub:

https://github.com/sxyseo/ai-agent-from-scratch

(注:这个仓库是示例性的,如果你 fork 了需要替换为自己的内容)

什么时候不应该用 Agent

最后说一个反直觉的结论:不是所有场景都需要 Agent

如果你的应用场景是”用户问一个固定类型的问题,用一个固定的 API 解决”,直接用 Function Calling 单次调用就够了。Agent 的价值在于需要多步推理和工具组合的场景

判断标准很简单:用户的一个问题,是否需要查多个来源、做多次计算、或者根据中间结果决定下一步行动?如果是,用 Agent。如果不是,别加这个复杂度。

参考来源:OpenAI Function Calling 文档(https://platform.openai.com/docs/guides/function-calling);DuckDuckGo Instant Answer API 文档(https://duckduckgo.com/api);OpenAI 2025 年 ​开发者大会关于 Agent 的最佳实践分享。

喜欢 (0)
[🍬谢谢你请我吃糖果🍬🍬~]
分享 (0)
关于作者:
少将,关注Web全栈开发、项目管理,持续不断的学习、努力成为一个更棒的开发,做最好的自己,让世界因你不同。