大模型实操Agent-如何从0开始搭建function calling

构建Agent的重要的一步是Function calling(函数调用),本文不使用任何langchain等框架或者coze等平台,从0开始构建一个可以调用function的Agent。

Function calling其实就是提供了一种方式,允许LLM与外部系统进行交互,还有如何进行交互。

1、买火车票

我让Kimi帮买张火车票,它会直接拒绝,甚至换ChatGPT、文心、通义等其他大模型,得到的回答只有:"我无法直接为您购买火车票"。
不过,LLM是知道买票,但它不能直接操作,它的本质是语言模型,所以需要和外部系统进行交互,那么就需要函数调用(function call)

让Kimi帮买张火车票

2、Agent构建思路

首先理清思路,确定好Agent的目标、需要的function,其次理解用户问题、选择工具,最后将返回的结果,整理好给用户。

设计思路

2.1 场景示例-天气Agent

创建一个获取最新天气的Agent,以及如何使用function call。

  • Agent的目标:可以回答关于天气的问题。

  • function:调用某地方的天气情况,并且反馈

  • 流程

    • a.思考: 用户输入问题,LLM先对问题进行分析
    • b.行动: 如果问到了天气问题,则分析出需要调用的function以及function要传入的参数
    • c.响应:function返回后,将答案整理好回复给用户。
  • 函数自定

    • a.定义一个获取天气的函数(属于Tools中的一个,这里用于演示,不做真实调用):

       def get_weather(location): 
         return "天气晴朗"
      
    • b.再定义一个大模型的发送信息的方法:

      def send_messages(messages): 
        client = OpenAI(
                            api_key="<你的llm的key>", 
                            base_url="https://api.llm.com")
        response = client.chat.completions.create( 
                            model="llm-chat", 
                            messages=messages) 
        return response.choices[0].message
      

2.2 实操Agent-天气Agent

从“用户提出问题”到“思考”到“响应”调用了多次LLM模型,所以要求模型按照顺序去调用LLM:

一定需要理清思路!一定需要理清思路!
换句话说写好system prompt!换句话说写好system prompt!

以下是思考过程

你在运行一个“思考”,“工具调用”,“响应”循环。每次只运行一个阶段

1.“思考”阶段:你要仔细思考用户的问题
2.“工具调用阶段”:选择可以调用的工具,并且输出对应工具需要的参数
3.“响应”阶段:根据工具调用返回的影响,回复用户问题。

已有的工具如下:
get_weather:
e.g. get_weather:天津
返回天津的天气情况

Example:
question:天津的天气怎么样?
thought:我应该调用工具查询天津的天气情况
Action:
{
    "function_name":"get_response_time"
    "function_params":{
        "location":"天津"
    }
}
调用Action的结果:“天气晴朗”
Answer:天津的天气晴朗

提示工程

上边的逻辑正好可以当作system的提示工程:

system="""
    你在运行一个“思考”,“工具调用”,“响应”循环。每次只运行一个阶段

    1.“思考”阶段:你要仔细思考用户的问题
    2.“工具调用阶段”:选择可以调用的工具,并且输出对应工具需要的参数
    3.“响应”阶段:根据工具调用返回的影响,回复用户问题。

    已有的工具如下:
    get_weather:
    e.g. get_weather:天津
    返回天津的天气情况

    Example:
    question:天津的天气怎么样?
    thought:我应该调用工具查询天津的天气情况
    Action:
    {
        "function_name":"get_response_time"
        "function_params":{
            "location":"天津"
        }
    }
    调用Action的结果:“天气晴朗”
    Answer:天津的天气晴朗
"""

用户Query

第一步,向模型提问一个问题

question="北京天气怎么样"

messages = [{"role": "system", "content": system_prompt},
{"role": "user", "content": question}]

message = send_messages(messages)
print(f"Model-1th>\n {message.content}")

返回值:

Model-1th>
 thought:我应该调用工具查询北京的天气情况
Action:
{
        "function_name":"get_weather",
        "function_params":{
                "location":"北京"
        }
}

可以看出模型已经进行了思考,并且返回了可以调用的工具了

函数调用

第二步,如果从“第一步”的返回值中可以提取调用工具的json

 "function_name":"get_weather",
 "function_params": "location":"北京"

第三步,调用真实工具,获取真实结果

invoke_function(**function_name,**function_params)

返回结果

第四步,将工具调用的结果追加到message中,一起给到模型,让它总结回答:

messages.append({"role": "assistant", 
                 "content": f"调用Action的结果:{tianqi}"}) 
message = send_messages(messages)
print(f"result>\n {message.content}")

返回值:

result> 
北京今天的天气晴朗。

3、tools功能的演进

上篇文章提过,笔者认为function calling和tool using功能相同,只是描述不同,看每个人的语言习惯。
随着LLM调用工具的普及,这种调用方法集成在大模型api接口中就变得越重要。

大部分模型厂商已经支持了function call,下面是deepseek工具调用的一个例子:

response = client.chat.completions.create(
    model="deepseek-chat",
    messages=messages,
    tools=tools
)

其中 tools是可以供模型选择的工具。

写在最后

从0开发写function的逻辑,需要让模型思考、观察、行动。其实这个流程的循环其实就是ReAct框架的原理。

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容