程序调用大模型返回结构化输出(JSON)

本文涉及的产品
注册配置 MSE Nacos/ZooKeeper,118元/月
应用实时监控服务-可观测链路OpenTelemetry版,每月50GB免费额度
可观测监控 Prometheus 版,每月50GB免费额度
简介: 本文介绍了如何使用讯飞星火大模型API,并通过Python封装实现结构化数据输出。首先,通过封装SparkAI类,实现了与讯飞星火API的交互,确保了调用的安全性和便捷性。接着,利用Pydantic库定义了数据模型`CalendarEvent`,确保从大模型获取的回答能够被正确解析成预设的结构化JSON格式,从而解决了大模型回答不规范的问题。示例代码展示了如何构造请求、接收并解析响应,最终输出结构化的活动信息。

程序调用大模型返回结构化输出(JSON)

大家很多时候使用langchain、llamaindex等大模型框架的时候,一直很头疼的就是大模型的answer不一定就按照约定的结构化数据返回,那么下面就以讯飞的spark为例解决这个问题

星火模型调用类

按照OpenAI的sdk的格式,封装了讯飞星火的调用类如下:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
'''
@File    :   SparkApi.py
@Time    :   2024/08/08 15:30:54
@Author  :   CrissChan 
@Version :   1.0
@Site    :   https://blog.csdn.net/crisschan
@Desc    :   按照OpenAI的SDK格式封装的讯飞星火大模型的调用类
'''

import _thread as thread
import base64
import datetime
import hashlib
import hmac
import json
from urllib.parse import urlparse, urlencode
import ssl
from datetime import datetime
from time import mktime
from wsgiref.handlers import format_date_time
import websocket

class SparkAI:
    def __init__(self, appid, api_key, api_secret, spark_url, domain):
        self.appid = appid
        self.api_key = api_key
        self.api_secret = api_secret
        self.spark_url = spark_url
        self.domain = domain
        self.answer = ""

    def _create_url(self):
        # 生成URL的逻辑保持不变
        now = datetime.now()
        date = format_date_time(mktime(now.timetuple()))

        host = urlparse(self.spark_url).netloc
        path = urlparse(self.spark_url).path

        signature_origin = f"host: {host}\ndate: {date}\nGET {path} HTTP/1.1"
        signature_sha = hmac.new(self.api_secret.encode('utf-8'), signature_origin.encode('utf-8'),
                                 digestmod=hashlib.sha256).digest()
        signature_sha_base64 = base64.b64encode(signature_sha).decode(encoding='utf-8')

        authorization_origin = f'api_key="{self.api_key}", algorithm="hmac-sha256", headers="host date request-line", signature="{signature_sha_base64}"'
        authorization = base64.b64encode(authorization_origin.encode('utf-8')).decode(encoding='utf-8')

        v = {
            "authorization": authorization,
            "date": date,
            "host": host
        }
        return self.spark_url + '?' + urlencode(v)

    def _on_message(self, ws, message):
        data = json.loads(message)
        code = data['header']['code']
        if code != 0:
            print(f'请求错误: {code}, {data}')
            ws.close()
        else:
            choices = data["payload"]["choices"]
            status = choices["status"]
            content = choices["text"][0]["content"]
            print(content, end="")
            self.answer += content
            if status == 2:
                ws.close()

    def _gen_params(self, question):
        return {
            "header": {"app_id": self.appid, "uid": "1234"},
            "parameter": {
                "chat": {
                    "domain": self.domain,
                    "random_threshold": 0.5,
                    "max_tokens": 2048,
                    "auditing": "default"
                }
            },
            "payload": {
                "message": {
                    "text": [
                        {"role": "user", "content": question}
                    ]
                }
            }
        }

    def chat(self, question):
        self.answer = ""  # 重置答案
        ws_url = self._create_url()
        ws = websocket.WebSocketApp(
            ws_url,
            on_message=self._on_message,
            on_error=lambda ws, error: print("### error:", error),
            on_close=lambda ws, close_status_code, close_msg: print(" "),
            on_open=lambda ws: ws.send(json.dumps(self._gen_params(question)))
        )
        ws.run_forever(sslopt={"cert_reqs": ssl.CERT_NONE})
        return self.answer

调用SparkAI返回结构数据

将星火大模型的appid、api_secret、apikey都存放在.env配置文件中,通过dotenv将内容读出来存入变量中(_变量的介绍可以再次文章中参考https://blog.csdn.net/crisschan/article/details/133277855)

from dotenv import load_dotenv, find_dotenv
_=load_dotenv(find_dotenv())
appid = os.getenv("SPARK_APP_ID")
api_secret=os.getenv("SPARK_APP_SECRET")
api_key=os.getenv("SPARK_APP_KEY")

#用于配置大模型版本,默认"general/generalv2"
# domain = "general"   # v1.5版本
domain = "generalv2"    # v2.0版本
#云端环境的服务地址
spark_url = "ws://spark-api.xf-yun.com/v2.1/chat"  # v2.0环境的地址ws(s)://spark-api.xf-yun.com/v2.1/chat
    client = SparkAI(appid=appid, api_key=api_key, api_secret=api_secret, spark_url=spark_url, domain=domain)

下面设计结构化返回的内容如下代码。

import os
from pydantic import BaseModel

# 这个定义创建了一个数据模型,其中:
#name 必须是字符串
#date 必须是字符串
#participants 必须是字符串列表
class CalendarEvent(BaseModel):
        name: str
        date: str
        participants: list[str]

Pydantic是一个用于数据验证和设置管理的 Python 库,BaseModel 是 Pydantic 中的一个核心类,用于创建数据模型。通过继承 BaseModel,可以轻松定义具有类型提示的数据结构,这些模型可以自动验证数据,确保数据符合预期的格式和类型。使用 Pydantic 的 BaseModel 可以更容易地处理和验证从 AI 响应中提取的结构化数据,提高代码的可靠性和可读性。在通过如下代码的约束,就可以收获一个按照格式化好的json。

prompt = """
    系统:提取事件信息。
    用户:Alice和Bob将在周五参加科学展览会。
    请以JSON格式提供以下信息:
    {
        "name": "事件名称",
        "date": "事件日期",
        "participants": ["参与者列表"]
    }
    """

    response = client.chat(prompt)

    try:
        event_dict = json.loads(response)
        event = CalendarEvent(**event_dict)
        print(f"活动名称: {event.name}")
        print(f"日期: {event.date}")
        print(f"参与者: {', '.join(event.participants)}")
    except json.JSONDecodeError:
        print("无法解析响应为JSON格式")
    except Exception as e:
        print(f"处理响应时出错: {str(e)}")

运行后得到如下结果:

 {
  "name": "科学展览会",
  "date": "周五",
  "participants": ["Alice", "Bob"]
}  
活动名称: 科学展览会
日期: 周五
参与者: Alice, Bob
目录
相关文章
|
JSON 应用服务中间件 nginx
filebeat收集json格式的nginx程序日志(二)
filebeat收集json格式的nginx日志 1.为什么要收集json格式的日志类型 由于nginx普通日志收集过来的日志内容都是存在一个字段中的值,我们想单独对日志中的某一项进行查询统计,比如我只想查看某个IP请求了我那些页面,一共访问了多少次,在普通的日志中是无法过滤的,不是很满意
973 0
filebeat收集json格式的nginx程序日志(二)
|
5月前
|
JSON 数据格式
langchain 入门指南 - JSON 形式输出大模型的响应
langchain 入门指南 - JSON 形式输出大模型的响应
190 0
|
存储 JSON 自然语言处理
【ODPS新品发布第2期】实时数仓Hologres:推出计算组实例/支持JSON数据/向量计算+大模型等新能力
本期将重点介绍Hologres推出计算组实例,Hologres支持JSON数据 ,Hologres向量计算+大模型能力,Hologres数据同步新能力,Hologres数据分层存储
|
SQL JSON 安全
基于JSON的SQL注入攻击触发需要更新Web应用程序防火墙
基于JSON的SQL注入攻击触发需要更新Web应用程序防火墙
|
JSON JavaScript 前端开发
使用jQuery Mobile和JSON创建移动应用程序
  近来移动应用开发迅速受到很多公司的关注,他们寻求为现存的产品和应用程序添加移动展现或者“触点”。即便不是所有,大部分移动应用开发框架也都会适应某种现存的“桌面”开发平台。基于Web的框架则不同。业界当前采用jQuery来创建移动web应用程序(上个月发布了jQuery Mobile Alpha 3)。
1020 0
|
JSON JavaScript 前端开发
使用jQue“.NET研究”ry Mobile和JSON创建移动应用程序
  近来移动应用开发迅速受到很多公司的关注,他们寻求为现存的产品和应用程序添加移动展现或者“触点”。即便不是所有,大部分移动应用开发框架也都会适应某种现存的“桌面”开发平台。基于Web的框架则不同。业界当前采用jQuery来创建移动web应用程序(上个月发布了jQuery Mobile Alpha 3)。
895 0
|
关系型数据库 API PHP
如何禁用WordPress程序REST API功能且移除wp-json链接
WordPress是什么? WordPress是使用PHP语言开发的博客平台,用户可以在支持PHP和MySQL数据库的服务器上架设属于自己的网站。
1748 0
|
数据格式 XML
未能加载文件或程序集 Newtonsoft.Json, Version=4.5.0.0 的报错,解决方法
原文:未能加载文件或程序集 Newtonsoft.Json, Version=4.5.0.0 的报错,解决方法 使用httpclient测试webapi的时候客户端报错: {"未能加载文件或程序集“Newtonsoft.Json, Version=4.5.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed”或它的某一个依赖项。
2906 0
未能加载文件或程序集"Newtonsoft.Json, Version=4.5.0.0
  这问题遇到好几次了,重新更改了引用都不好使,有的时候版本改成一致就好了,但是有的地方你不知道在哪里用了就不好排查,所性在config里面加个配置让程序运行的时候去处理得了~ 很实用,放在configuration根节点下面就行了!   原文地址:http://www.
751 0