【Mini AI Agent】如何用 100 行代码构建一个最小智能体?

今年 3 月份,知名人工智能科学家吴恩达(Andrew Ng)在社交平台 X 上发帖提到, "我认为 AI 代理工作流程将在今年推动 AI 的大规模进步------甚至可能比下一代基础模型还要多......GPT-3.5(零样本)的正确率为 48.1%,GPT-4(零样本)为 67.0%,而在智能体循环中,GPT-3.5 的正确率高达 95.1%" 。此贴发出之后,引发了业界广泛关注。有人表示,这代表着 AI 发展中的范式转变。 本文首先对智能体(AI Agent)的概念做一个简单介绍,然后详细拆解一个仅用 100 行代码构建的极简智能体应用。

1 什么是智能体?

智能体(AI Agent) 是一种超越简单文本生成的人工智能系统,它使用大语言模型(LLM)作为其核心计算引擎,使其能够进行对话、推理、执行任务,展现一定程度的自主性。

在智能体架构中,核心功能可以归纳为三个步骤的循环:感知-决策-行动。智能体首先通过感知机制收集环境信息,然后基于该信息和预设目标,通过决策机制制定行动计划,最终通过动作执行机制实施这些计划。

图:智能体架构示意

2 示例:Mini AI Agent

了解了智能体的概念,接下来我们一起一步步拆解一个仅用 100 行代码构建的最小智能体应用,耗时约 1 个小时。

2.1 效果演示

先来看一下效果演示,

图:Mini AI Agent应用演示

看似平淡无奇的两次问答,实际上已经体现了智能体的核心循环:感知-决策-行动。

  • 感知:接收问题
  • 决策:理解问题,确定目标,然后通过推理决定使用何种工具(即制定计划)
  • 行动:使用工具获取信息,然后生成答案

2.2 环境准备

申请账号:百度千帆

  1. 访问百度智能云千帆,注册账号并登录千帆大模型控制台
  2. 打开模型服务-应用接入页面,创建应用,记下 API Key 和 Secret Key 备用
  3. 打开模型服务-在线服务页面,找到 ERNIE-3.5-8K(支持函数功能),开通付费(不用担心,非常便宜,100 次调用才 2 毛钱)

PS: 本文只是以百度千帆为例,大家可以根据自身经验,替换成其他任何支持函数功能的大模型,比如智谱清言、Azure、OpenAI等。

搭建本地开发环境:Jupyter Notebook

  1. 访问 Anaconda 官网,下载安装包并安装 Anaconda
  2. 命令行运行 conda install jupyter notebook,安装 Jupyter Notebook
  3. 打开 GitHub 示例项目 emac/langchain-samples,git clone 到本地
  4. 命令行打开 langchain-samples 目录,运行 jupyter notebook,打开 Jupyter Notebook

2.3 程序拆解

准备好环境之后,就可以进入程序员最喜欢的实操环节了!

安装依赖

diff 复制代码
!pip install langchain langchain-community langchain-core gradio

依赖说明:

  • langchain: 最著名的开发大语言模型应用的开源框架,没有之一
  • gradio: 一个用于快速构建机器学习模型的交互式 Web 应用的 Python 库

初始化大模型

ini 复制代码
from langchain_community.llms import QianfanLLMEndpoint
import os
​
print("# 初始化千帆")
os.environ["QIANFAN_AK"]='千帆应用的 API Key'
os.environ["QIANFAN_SK"]='千帆应用的 Secret Key'
​
llm = QianfanLLMEndpoint(streaming=True,
                         model="ERNIE-3.5-8K",
                         temperature=0.1)
​
response = llm.invoke("上海春天一般哪个月开始?")
print(response)

执行之前先替换之前记录的千帆应用的 API Key 和 Secret Key。

程序解读:

  1. 初始化环境变量,创建一个千帆 LLM 实例
  2. 发起(人生)第一次大模型 API 调用,如果不成功则返回检查环境和依赖

定义函数

python 复制代码
from langchain import PromptTemplate, LLMChain
from langchain.chains import LLMRequestsChain
from langchain_core.tools import tool
from langchain_core.utils.function_calling import convert_to_openai_tool
​
print("# 定义函数")
@tool
def search_ip(question:str, ip:str) -> str:
    """
    首先获取输入的IP地址的位置信息,然后回答输入的问题
    @param question: 问题
    @param ip: IP地址
    """
    prompt_template = """以下是IP地址'{ip}'的位置信息:
    >>> {requests_result} <<<
    根据以上位置信息,回答以下这个问题:
    >>> {question} <<<"""
    prompt = PromptTemplate(
        input_variables=["question", "ip", "requests_result"],
        template=prompt_template
    )
    chain = LLMRequestsChain(llm_chain = LLMChain(llm=llm, prompt=prompt))
    inputs = {
        "question": question,
        "ip": ip,
        "url": "https://api.songzixian.com/api/ip?dataSource=generic_ip&ip=" + ip
    }
    return chain.invoke(inputs)
​
@tool
def search_phone(question:str, phone:str) -> str:
    """
    首先获取输入的手机号码的归属地信息,然后回答输入的问题
    @param question: 问题
    @param phone: 手机号码
    """
    prompt_template = """以下是手机号码'{phone}'的归属地信息:
    >>> {requests_result} <<<
    根据以上归属地信息,回答以下这个问题:
    >>> {question} <<<"""
    prompt = PromptTemplate(
        input_variables=["question", "phone", "requests_result"],
        template=prompt_template
    )
    chain = LLMRequestsChain(llm_chain = LLMChain(llm=llm, prompt=prompt))
    inputs = {
        "question": question,
        "phone": phone,
        "url": "https://api.songzixian.com/api/phone-location?dataSource=phone_number_location&phoneNumber=" + phone
    }
    return chain.invoke(inputs)
​
functions=[convert_to_openai_tool(search_ip)['function'],convert_to_openai_tool(search_phone)['function']]
print(functions)

程序解读:

  • 定义 search_ipsearch_phone 两个工具函数,背后连接一个免费的第三方 API 接口平台,用来获取指定 IP 的位置信息和指定手机号的归属地信息,然后再结合原始问题,通过大模型生成最终回答。
  • tool/ convert_to_openai_tool: 用于生成函数定义的注解和工具方法
  • PromptTemplate: Prompt 模板,支持变量
  • LLMRequestsChain: 一个基于 URL 请求的大模型链,先调用 URL 获取数据,然后将数据传给一个已绑定 Prompt 的大模型链获取答案

绑定函数

python 复制代码
from langchain.schema import HumanMessage
from langchain_community.chat_models import QianfanChatEndpoint
import json
​
# 此处不能设定streaming=True,否则无法激活函数回调
chat = QianfanChatEndpoint(model="ERNIE-3.5-8K",
                          temperature=0.1)
​
def get_response(message):
    print("# 绑定函数")
    result = chat.invoke([HumanMessage(content=message)],
                               functions=functions)
    print(result)
​
    function_call_info = result.additional_kwargs.get("function_call", None)
    print(function_call_info)
    
    if not function_call_info:
        print("# 直接返回")
        return result.content
​
    print("# 调用函数")
    function_name = function_call_info["name"]
    function_args = json.loads(function_call_info["arguments"])
    function_result = eval(function_name)(function_args)
    print(function_result)
    return function_result["output"]

程序解读:

  • 创建一个千帆 Chat 实例,绑定之前定义的两个函数,传入用户输入的问题(对应智能体的感受 环节),然后发起调用,根据调用结果执行不同任务(对应智能体的决策环节):

    • 如果调用结果没有提示函数调用(result.additional_kwargs.get("function_call", None)),则直接返回结果
    • 否则,根据大模型提示的函数名和请求参数,通过反射调用相应的函数(对应智能体的执行环节),然后返回结果

构建应用

ini 复制代码
import gradio as gr
​
def submit(message, chat_history):
    bot_message = get_response(message)
    # 保存历史对话记录,用于显示
    chat_history.append((message, bot_message))
    return "", chat_history
​
print("# 创建交互")
with gr.Blocks() as demo:
    chatbot = gr.Chatbot(height=240) # 对话框
    msg = gr.Textbox(label="Prompt") # 输入框
    submitBtn = gr.Button("Submit") # 提交按钮
    clearBtn = gr.ClearButton([msg, chatbot]) # 清除按钮
    # 提交
    msg.submit(submit, inputs=[msg, chatbot], outputs=[msg, chatbot]) 
    submitBtn.click(submit, inputs=[msg, chatbot], outputs=[msg, chatbot])
    
gr.close_all()
demo.launch()

程序解读:

  • 创建智能体应用,构建一个用户和智能体的对话框,将用户输入的消息传给后台创建的大模型,实时获取响应
  • 此处可以看到,借助 Gradio 框架,短短几行代码,就可以构建出一个简洁的对话框应用,非常 Nice!

至此,一个极简的智能体应用就构建成功了,前后仅用 100 行代码。完整代码参见 GitHub

彩蛋:智能体如何思考?

看完演示,拆解完程序,你可能对智能体里的 "智能" 两字的理解还是有点模模糊糊。其实要真正理解这一点,光看程序还不够,得看下大模型的响应结果(见下)。注意其中 response_metadata 有一个很有意思的 thoughts 字段,其代表了大模型的思考过程,是不是和人类非常类似?

ini 复制代码
# 问题:120.230.93.202属于哪个城市?
# 大模型响应结果
additional_kwargs={'finish_reason': 'function_call', 'request_id': 'as-fcd0dz2mmg', 'object': 'chat.completion', 'search_info': [], 'function_call': {'name': 'search_ip', 'arguments': '{"question":"120.230.93.202属于哪个城市?","ip":"120.230.93.202"}'}} 
​
response_metadata={'token_usage': {'prompt_tokens': 194, 'completion_tokens': 63, 'total_tokens': 257}, 'model_name': 'ERNIE-3.5-8K', 'finish_reason': 'function_call', 'id': 'as-fcd0dz2mmg', 'object': 'chat.completion', 'created': 1714900176, 'result': '', 'is_truncated': False, 'need_clear_history': False, 'function_call': {'name': 'search_ip', 'thoughts': '用户想要知道一个特定IP地址的地理位置信息,我需要使用search_ip工具来获取这个信息。', 'arguments': '{"question":"120.230.93.202属于哪个城市?","ip":"120.230.93.202"}'}, 'usage': {'prompt_tokens': 194, 'completion_tokens': 63, 'total_tokens': 257}} id='run-0706ab6f-a21f-4352-bbb1-1f1a0f13c426-0'

3 小结

本文基于百度千帆大模型,使用 Langchain 和 Gradio 框架,用短短 100 行代码就构建出一个极简的智能体应用。该应用能够根据用户问题,选择不同的工具获取信息,并生成最终回答,体现了智能体最核心的三步循环:感知-决策-行动。基于这个演示应用,相信聪明的你可以构建出更复杂、更智能的智能体应用,欢迎留言交流。

4 参考

相关推荐
NAGNIP1 天前
一文搞懂深度学习中的通用逼近定理!
人工智能·算法·面试
冬奇Lab1 天前
一天一个开源项目(第36篇):EverMemOS - 跨 LLM 与平台的长时记忆 OS,让 Agent 会记忆更会推理
人工智能·开源·资讯
冬奇Lab1 天前
OpenClaw 源码深度解析(一):Gateway——为什么需要一个"中枢"
人工智能·开源·源码阅读
AngelPP1 天前
OpenClaw 架构深度解析:如何把 AI 助手搬到你的个人设备上
人工智能
宅小年1 天前
Claude Code 换成了Kimi K2.5后,我再也回不去了
人工智能·ai编程·claude
九狼1 天前
Flutter URL Scheme 跨平台跳转
人工智能·flutter·github
ZFSS1 天前
Kimi Chat Completion API 申请及使用
前端·人工智能
warm3snow1 天前
Claude Code 黑客马拉松:5 个获奖项目,没有一个是"纯码农"做的
ai·大模型·llm·agent·skill·mcp
天翼云开发者社区1 天前
春节复工福利就位!天翼云息壤2500万Tokens免费送,全品类大模型一键畅玩!
人工智能·算力服务·息壤
知识浅谈1 天前
教你如何用 Gemini 将课本图片一键转为精美 PPT
人工智能