走进科学疑难问题出现
在调试fastapi程序的时候,报错碰到422错误
INFO: 192.168.0.99:46536 - "POST /v1/chat/completions/ HTTP/1.1" 422 Unprocessable Entity
干净利索,只有这一句报错,不管代码里加入多少print语句查看中间变量,都只有这一句话,就像根本没有走下去(事实上确实没有走下去)。
使用的测试语句是:
curl -X 'POST' \'http://192.168.0.99:8000/v1/chat/completions/' \-H 'accept: application/json' \-d "{'msg':[ {'role': 'user', 'content': 'hello'} ]}"
不知道问题在哪,后来专门找了另一条测试语句,并写了/items/的代码,测试通过,没报错:
curl -X POST http://192.168.0.99:8000/items/ -H "Content-Type: application/json" \
-d '{"name": "Bruce_Lee", "description":"this is a book of Bruce_Lee", "price": 12}'
解决问题
然后就比对代码,比对/items/ 和/v1/chat/completions/ 里面代码的书写,甚至比对着写了个最简单的json数据,把这句"{'msg':[ {'role': 'user', 'content': 'hello'} ]}"学着对的那句简化成-d '{"msg": "hello"}
你还别说,输出了很多打印信息,证明程序跑下去了。当然因为简化json,中间还改了程序,再用回长段json的时候再改回去,总之是改的头昏眼花。
后来到文心一言里,让它帮我把单引号改成双引号,
提问:
请把单括号换成双括号{'msg':[ {'role': 'user', 'content': 'hello'} ]} (这里因为头昏,错把引号说成了括号,索性一言给我了份正确格式的答案)
回答
当您提到将“单括号”换成“双括号”时,我猜测您是指将某种不规范的或错误的括号使用方式更正为标准的 JSON 格式。在 JSON 中,对象(即键值对的集合)应该使用大括号
{}
来表示,而数组(即有序值的集合)应该使用方括号[]
来表示。根据您提供的示例
{'msg':[ {'role': 'user', 'content': 'hello'} ]}
,这实际上已经是一个有效的 JSON 字符串(尽管它可能被错误地称为“单括号”或“双括号”,这取决于上下文和解释者的视角)。在这个字符串中:
- 最外层的
{}
表示一个 JSON 对象。msg
是这个对象的一个键,其对应的值是一个 JSON 数组。- 这个数组由方括号
[]
表示,并且包含一个 JSON 对象。- 这个内部的对象又由大括号
{}
表示,并且有两个键值对:role
和content
。如果您的意图是确保这个 JSON 字符串被正确地用于
curl
命令中,那么您应该这样做:curl -X 'POST' \'http://192.168.0.99:8000/v1/chat/completions/' \-H 'accept: application/json' \-H 'Content-Type: application/json' \-d '{"messages":[{"role": "user", "content": "hello"}]}'
使用这份curl格式的代码,终于消除了422报错!
后来才知道(也许我以前就知道,只是忘记了) ,json一定要用双引号!
在 JSON 格式中,字符串值必须使用双引号 "
来界定,而不能使用单引号 '
。这是 JSON 标准的一部分,它确保了数据的一致性和可解析性。
json编程,砥砺前行!
总结下
这回对json格式算是印象深刻了,主要是受了python的影响,单引号和双引号混用,这才阴沟里翻了船。
最终问题解决,且原来计划的使用树莓派启动fastapi 中转openai指令,使ESP32开发板能够连上大模型,方案也被验证可行。
目前将fastapi 中转openai放置于树莓派2服务器上,
fastapi 中转openai配置
fastapi的代码为:
cat app.py
from typing import Annotated
import json
from fastapi import FastAPI, Path, Query
from pydantic import BaseModel
from openai import OpenAIimport os
import time
from typing import Union, Optionalapp = FastAPI()
API_KEY = "xxxx"
model="ernie-speed-8k"class Item(BaseModel):name: strdescription: str | None = Noneprice: floattax: float | None = Noneclass ChatItem(BaseModel):messages: listmodel: str | None = Nonedef ChatCompletions(messages: list,model: str,# provider: Optional[ProviderType] = None,stream: Optional[bool] = False,# proxy: Optional[str] = None,response_format: Optional[dict] = None,max_tokens: Optional[int] = None,stop: Optional[Union[list[str], str]] = None,api_key: Optional[str] = None,# ignored: Optional[list[str]] = None,# ignore_working: Optional[bool] = False,ignore_stream: Optional[bool] = False,**kwargs):client = OpenAI(api_key=api_key, # 含有 AI Studio 访问令牌的环境变量,https://aistudio.baidu.com/account/accessToken,base_url="https://aistudio.baidu.com/llm/lmapi/v3", # aistudio 大模型 api 服务域名
)chat_completion = client.chat.completions.create(messages=messages,model=model,
)print(f"==log for app chat_completion:{chat_completion} ")# response = chat_completion.result# print(f"==log for app response:{response}")return chat_completion@app.get("/items/{item_id}")
async def read_items(item_id: Annotated[int, Path(title="The ID of the item to get")],q: Annotated[str | None, Query(alias="item-query")] = None,
):results = {"item_id": item_id}if q:results.update({"q": q})return results@app.post("/v1/chat/completions/")
async def chat_completions(chatitem: ChatItem):print(chatitem)chatitemdict = chatitem.dict()print(f"==== items:{chatitemdict}")#print ("=" * 20 , messages, type(messages))#yjson = json.loads(messages)#print("="*10, yjson, type(yjson))#zmessages = yjson["messages"]#print("="*10, zmessages, typr(zmessages))model="ernie-speed-8k"messages = chatitem.messagesprint(f"==== messages=chatitem.msg:{messages}")y = ChatCompletions(messages=messages, model=model, api_key=API_KEY)print("="*10, y)z = y.choices[0].message.contentreturn z@app.post("/items/")
async def create_item(item: Item):print(f"==== get the Item type:{type(item)}, item:{item}")item_dict = item.dict()print(f"==== item_dict{item_dict}")if item.tax:price_with_tax = item.price + item.taxitem_dict.update({"price_with_tax": price_with_tax})return item_dict
需要到AiStudio官网获取apk_key并填入上面的代码中,在树莓派启动fastapi服务器:
uvicorn app:app --host 0.0.0.0 --reload
最终测试命令为:
curl -X 'POST' \'http://192.168.0.99:8000/v1/chat/completions/' \-H 'accept: application/json' \-H 'Content-Type: application/json' \-d '{"messages":[{"role": "user", "content": "hello"}]}'
省略一条head,这个命令也可以:
curl -X 'POST' \'http://192.168.0.99:8000/v1/chat/completions/' \-H 'Content-Type: application/json' \-d '{"messages":[{"role": "user", "content": "hello"}]}'
ESP32S3开发板通过中转连星河大模型
在ESP32S3开发板的MicroPython环境下,使用requests来fastapi的中转服务器,连接成功:
import requestsresponse = requests.post('http://192.168.0.99:8000/v1/chat/completions/',headers = {'Content-Type': 'application/json',},json = {'model': "ernie-speed-8k","messages": [{"role": "user", "content": "hello"}]}
)print(response.status_code, response.reason)
print(response.text)
输出显示:
>>> print(response.status_code, response.reason)
200 b'OK'
>>> print(response.text)
"你好!请问有什么需要我帮助你的吗?"
太棒了,使用这种解决方案,在家里使用一台树莓派做中转,就可以让家里的物联网设备都使用百度星河大模型啦!