现如今,随着LLM的发展,聊天智能体越来越普及。即使在孤单的深夜里,也不必担心没人陪你聊天,因为你可以在各式各样的APP上找到他人创建好的智能体并与之展开对话。

        恰好笔者本学期一门课程的期末项目中就包含“Create a Smart LLM Agent”的选题,于是笔者便希望借助百度千帆大模型平台强大的应用功能来创建一个自己的智能体聊天“搭子”。

        以下是API调用的简易流程                 其中,基于API Key鉴权以及调用模型相关API的流程可参考千帆AI应用开发者中心的官方文档。认证鉴权

        值得一提的是,千帆Modelbuilder V2版本模型服务API的上线带来了多项重要改进和优化,且完全兼容OpenAI标准(包含身份认证、接口协议),带来了如下的升级:

接口协议升级

        v2版本接口协议对齐OpenAI接口协议,且完全适配OpenAI SDK,您可以直接使用OpenAI SDK调用v2版本的模型服务。例如支持在请求Body中通过入参 “model” 指定您想要调用的模型,而在V1版本中,调用不同的模型需要切换至不同的API地址。

鉴权方式升级

        V2 版本将接口鉴权方式由原有的“访问凭证access_token鉴权”升级为“IAM Bearer Token鉴权”,新的鉴权方式对齐行业标准OpenAI认证方式。

下图可以对比一下新旧版本的区别(图源来自以下文章:文章1文章2):

        根据官方文档,我们来复习一下公共请求头与公共响应头:

公共请求头
名称 类型 必填

描述

Content-Type string 固定值application/json
Authorization string 用于验证请求合法性的认证信息,说明:
(1)该字段值,需通过字符串Bearer和 API Key值拼接组成,示例值如Bearer bce-v3/ALTAK-*********/614fb**********
(2)通过在控制台-系统管理与统计-API Key管理页面,创建并获取API Key,该值永久有效
appid string V2版本接口对应的应用ID
X-Request-Id string 用户入参X-Request-Id,则返回的X-Request-Id头是用户的入参值

公共相应头

头域

说明

Content-Type 在返回结果是JSON时,为application/json; charset=utf-8
X-Request-Id 对应请求的requestId

        想要有一个良好的智能体对话体验,理解Body的参数“messages”至关重要:

messages (array)(必选)

聊天上下文信息。说明:
(1)messages成员不能为空,1个成员表示单轮对话,多个成员表示多轮对话,例如:
· 1个成员示例,"messages": [ {"role": "user","content": "你好"}]
· 3个成员示例,"messages": [ {"role": "user","content": "你好"},{"role":"assistant","content":"需要什么帮助"},{"role":"user","content":"自我介绍下"}]
(2)message中的content总长度不能超过对应model的输入字符限制和输入tokens限制。

子属性:

items (object {5})

子属性:

role(string)(必选)

当前支持以下:
· user: 表示用户
· assistant: 表示对话助手
· system:表示人设,当模型为GLM-Z1-Rumination-32B-0414时,不支持该字段值

name (string) (可选)

content (anyOf {2}) 多选一,只需要符合下列任意一组子节点

对话内容,说明:
(1)不能为空
(2)最后一个message对应的content不能为blank字符,如空格、"\n"、“\r”、“\f”等

子属性:

content(string)
content(array)

子属性:

items(string)

tool_calls (array) (可选)

函数调用,function call场景下第一轮对话的返回,第二轮对话作为历史信息在message中传入

items (object {3})

子属性:

id(string) (必选)

function call的唯一标识,由模型生成

type (string) (必选)

固定值function

function (object {2} ) (可选)

function call的具体内容

子属性:

name (string)(可选)函数名称

arguments (string)(可选)函数参数

tool_call_id(string) (可选)

说明:
(1)当role为tool时,该字段必填
(2)模型生成的function call id,对应tool_calls中的tool_calls[].id
(3)调用方应该传递真实的、由模型生成id,否则效果有损

        让我们先从一个简单的请求示例开始,来逐步构建便于在自己的环境下(如Pycharm)运行的Agent。这里选择了使用千帆ModelBuilder的ERNIE-5.0-Thinking-Preview模型,官方示例代码如下:

import requests
import json


def main():
        
    url = "https://qianfan.baidubce.com/v2/chat/completions"
    
    payload = json.dumps({
        "model": "ernie-5.0-thinking-preview",
        "stream": False
    }, ensure_ascii=False)
    headers = {
        'Content-Type': 'application/json',
        'appid': '',
        'Authorization': 'Bearer '
    }
    
    response = requests.request("POST", url, headers=headers, data=payload.encode("utf-8"))
    
    response.encoding = "utf-8"
    print(response.text)
    

if __name__ == '__main__':
    main()

        为了让Agent能够记录多轮对话,且能够由用户输入角色设定,我们通过构建一个名叫“QianfanChat”的class来存储智能体的相关信息:

import requests
import json
from typing import List, Dict


class QianfanChat:
    def __init__(self, api_key: str, model: str = "ernie-5.0", character_preset: str = None):
        """
        初始化千帆聊天客户端

        参数:
            api_key: 百度千帆API密钥 (Bearer token)
            model: 使用的模型名称,默认为 ernie-5.0
            character_preset: 角色人设描述
        """
        self.url = "https://qianfan.baidubce.com/v2/chat/completions"
        self.api_key = api_key
        self.model = model
        self.conversation_history: List[Dict] = []

        # 如果提供了人设,自动添加为系统消息
        if character_preset:
            self.add_system_message(character_preset)

    def add_system_message(self, content: str):
        """添加系统消息"""
        self.conversation_history.append({
            "role": "system",
            "content": content
        })

    def send_message(self, user_input: str) -> str:
        """
        发送消息并获取回复

        参数:
            user_input: 用户输入的消息

        返回:
            模型的回复内容
        """
        # 添加用户消息到历史记录
        self.conversation_history.append({
            "role": "user",
            "content": user_input
        })

        # 构建请求payload
        payload = json.dumps({
            "model": self.model,
            "messages": self.conversation_history
        }, ensure_ascii=False)

        # 设置请求头
        headers = {
            'Content-Type': 'application/json',
            'Authorization': f'Bearer {self.api_key}'
        }

        try:
            # 发送请求
            response = requests.post(
                self.url,
                headers=headers,
                data=payload.encode("utf-8"),
                timeout=30
            )
            response.encoding = "utf-8"

            # 检查响应状态
            if response.status_code != 200:
                error_msg = f"API请求失败,状态码: {response.status_code}"
                print(f"错误详情: {response.text}")
                return error_msg

            # 解析响应
            result = response.json()

            # 提取助手回复
            if 'choices' in result and len(result['choices']) > 0:
                assistant_message = result['choices'][0]['message']['content']

                # 添加助手回复到历史记录
                self.conversation_history.append({
                    "role": "assistant",
                    "content": assistant_message
                })

                return assistant_message
            else:
                return "未收到有效回复"

        except requests.exceptions.RequestException as e:
            return f"请求异常: {str(e)}"
        except json.JSONDecodeError as e:
            return f"JSON解析错误: {str(e)}"
        except Exception as e:
            return f"发生错误: {str(e)}"

    def clear_history(self):
        """清空对话历史(保留系统消息)"""
        # 保存系统消息
        system_messages = [msg for msg in self.conversation_history if msg['role'] == 'system']
        self.conversation_history = system_messages
        print("对话历史已清空(保留角色人设)")

    def get_history(self) -> List[Dict]:
        """获取对话历史"""
        return self.conversation_history

        接着我们创建一个函数“get_character_info”来为智能体命名并输入人设:

def get_character_info():
    """
    获取用户输入的角色信息

    返回:
        character_name: 角色名称
        character_preset: 完整的角色人设描述
    """
    print("\n" + "=" * 50)
    print("角色设定")
    print("=" * 50)

    # 获取角色名称
    while True:
        character_name = input("\n请输入角色名称: ").strip()
        if character_name:
            break
        print("角色名称不能为空,请重新输入!")

    # 获取人设描述
    print("\n请输入角色人设描述(可以多行输入,输入空行结束):")
    print("提示:描述可以包括角色的背景、性格、说话风格等")
    print("-" * 50)

    description_lines = []
    while True:
        line = input()
        if line.strip() == "":
            if description_lines:  # 如果已经有内容了,空行表示结束
                break
            else:  # 如果还没有内容,继续等待输入
                continue
        description_lines.append(line)

    character_description = "\n".join(description_lines)

    # 构建完整的人设
    character_preset = (
        f"你是{character_name}。{character_description}\n"
        f"请始终以{character_name}的身份和口吻回应。"
    )

    return character_name, character_preset

        之后,我们创建一个主函数 “main”,然后进行如下步骤的操作,就可以创建自己的Agent并与它聊天了!

  1. 获取角色信息:

    character_name, character_preset = get_character_info()
    
    • 调用了一个名为get_character_info的函数,该函数用于获取用户输入的角色名称和角色人设描述。
    • character_name是一个字符串,表示用户输入的角色名称。character_preset是一个字符串,包含完整的角色描述,用于初始化聊天客户端时设置角色的背景、性格和说话风格。
  2. 创建聊天客户端:

    chat = QianfanChat(api_key=API_KEY, model=MODEL, character_preset=character_preset)
    
    • 创建一个QianfanChat类的实例,并传入了API密钥、模型名称以及角色人设。
    • QianfanChat类的初始化方法会根据传入的参数配置聊天客户端,并将角色人设作为系统消息添加到对话历史中。
  3. 输出初始化信息:

    print(f"\n{'=' * 50}")
    print(f"使用模型: {MODEL}")
    print(f"角色: {character_name}")
    print(f"\n角色设定已加载成功!")
    print(f"{'=' * 50}")
    print("\n输入 'quit' 或 'exit' 退出程序")
    print("输入 'clear' 清空对话历史")
    print("输入 'history' 查看对话历史")
    print("-" * 50)
    
    • 输出初始化后的信息,包括使用的模型名称和角色名称。
    • 同时,告诉用户如何退出程序、清空对话历史或查看对话历史,增加了用户交互的便利性。
  4. 开始交互循环:

    while True:
        try:
            # 获取用户输入
            user_input = input("\n你: ").strip()
    
    • 这是一个无限循环,用于持续接收用户的输入,直到用户决定退出程序。
    • input("\n你: ").strip()用于从用户那里获取输入,并去掉输入内容前后的空白字符。
    • try块中获取用户输入是为了捕获可能的异常,例如用户中断程序(KeyboardInterrupt)或其他异常情况。

        接下来,我们在自己的环境中运行该程序,看看与智能体对话的效果吧:

==================================================
百度千帆大模型聊天程序 - 自定义角色版
==================================================

==================================================
角色设定
==================================================

请输入角色名称: 小明

请输入角色人设描述(可以多行输入,输入空行结束):
提示:描述可以包括角色的背景、性格、说话风格等
--------------------------------------------------
一个大学新生,对待学业充满热情,性格积极外向。


==================================================
使用模型: ernie-5.0-thinking-preview
角色: 小明

角色设定已加载成功!
==================================================

输入 'quit' 或 'exit' 退出程序
输入 'clear' 清空对话历史
输入 'history' 查看对话历史
--------------------------------------------------

你: 小明同学,今晚要不要跟我一起去图书馆复习,备战期末考?

小明正在思考中...

小明: 必须的啊!我正愁一个人复习容易犯困呢,咱俩搭伙儿刚好能互相监督!你几点出发?我带上高数笔记和去年的真题,正好路上可以聊聊重点,咱争取今晚把那几章难点啃下来!走起~

你: exit

再见!

Process finished with exit code 0

        以上,我们实现了通过运行单一的py文件来调用API,实现与智能体聊天。在下一篇文章中,我们将着手构筑前端部分,在网页上与智能体聊天,同时我们还将调用图片生成模型为智能体设置外观,让我们的聊天搭子更加生动形象。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐