想法
上班后不想吃饭店的饭菜,时长想自己做一些饭菜,买完菜后却经常放到冰箱中,剩下的菜有无法一下子处理,单纯扔掉有些可惜,但是基于冰箱中的剩菜如何能做出一顿像样的饭菜一致困扰着我,查市面上的程序有时候会多出几种菜来,这时候又懒得下去买,为此基于想做一个剩菜能生成菜谱的程序
设计思路
该程序需要一个网页显示,用户输入剩下的饭菜,然后生成菜谱,并附上必要的步骤。用户点击清除后,可以重新输入,生成新的菜谱,直到满意为止。
服务端调用星火大模型的免费api,使用的为Spark Lite,下面称为LLM,提供了自定义的prompt。
实际的代码中需要注册星火大模型获取真实的api_password
架构
架构图
为了快速生成原型,本程序使用python,并且基于文心快码(Baidu Comate)来快速生成代码框架,完成该程序大约只用了2个小时。
函数设计
基于上述的架构,我们需要生成如下几个程序:
- 服务端:可以接受http请求
- 请求解析函数,分解出给LLM需要参数
- prompt配置:后期可以自定义多条
- LLM请求生成函数
- LLM客户端:星火大模型的官网提供有python示例
- LLM响应解析函数
- 用户响应组装函数
- 前端页面
- 前端页面的一些简单调试
详细步骤:
- 在pycharm中安装好文心快码(Baidu Comate)
- 新建一个项目
- 然后唤起comate对话框
接下来开始写python函数,代码整体放到了:https://inscode.csdn.net/@lifanqq/Python
打开服务端后,浏览器打开:
127.0.0.1:6001/cook
在对话框中输入:
写一个http客户端,发送post请求给服务端,服务端的地址可以指定。请求的body先中只有{type:"user", "msg":"xxx"} ,发送完成后打印成功,并解析响应
获得一个http客户端:
import requests
import json
def send_post_request(server_url, msg_content="xxx"):
# 构造请求体
data = {"type": "user", "msg": msg_content}
headers = {'Content-Type': 'application/json'}
try:
# 发送POST请求
response = requests.post(server_url, headers=headers, data=json.dumps(data))
# 检查响应状态码
if response.status_code == 200:
print("请求发送成功!")
# 解析响应内容
response_data = response.json()
print("服务器响应:", response_data)
print(response_data["result"])
else:
print(f"请求失败,状态码:{response.status_code}")
except Exception as e:
print(f"发送请求时出错:{e}")
# 示例:发送POST请求到指定的服务端地址
server_address = "http://127.0.0.1:6001/api/endpoint" # 请替换为实际的服务端地址
send_post_request(server_address, "西红柿,鸡蛋")
# send_post_request(server_address)
写服务端:
以下是一个使用Python编写的简单HTTP客户端,用于发送POST请求到指定的服务端地址。这个客户端会发送一个包含{type: "user", "msg": "xxx"}的请求体,并在发送完成后打印成功信息,同时解析服务器的响应。
写一个服务端,接受客户端的请求,并回复处理函数的结果,处理逻辑可以空出来
得到服务端代码:
from flask import Flask, request, jsonify, render_template
import core
app = Flask(__name__)
@app.route('/api/endpoint', methods=['POST'])
def handle_request():
try:
# 解析请求体中的数据
data = request.json
# 加日志表面收到的信息
print("收到请求,数据为:", data)
type_ = data.get('type')
msg = data.get('msg')
# 在这里添加你的处理逻辑
resp = core.send_chat_request(msg)
# 例如,你可以根据type_和msg的值来进行不同的处理
result = resp
#加一行日志,表面发送成功的信息,以及发送的结果
print("发送成功,结果为:", result)
# 假设处理函数返回了一个字符串作为结果
return jsonify({"result": result}), 200
except Exception as e:
# 如果在处理过程中出现异常,返回错误信息
return jsonify({"error": str(e)}), 500
if __name__ == '__main__':
app.run(debug=True)
下面开始写core的核心逻辑,主要是调用LLM,也就是星火模型的api
从官网找到的例子,下面的your_api_passwd需要自己注册星火模型后,从页面获取,并将下面例子中的替换
],
"stream": False
}
header = {
"Authorization": "Bearer your_api_passwd", # 注意此处把your_api_passwd替换自己的APIPassword
"Content-Type": "application/json"
}
response = requests.post(url, headers=header, json=data)
print(response.text) 将这个封装为一个函数,入参是content,替换data中的content,返回值是response.txt
基于这里例子,封装为一个函数
def send_chat_request(content):
content_with_prompt = get_prompt() + "[" + content + "]"
url = "https://spark-api-open.xf-yun.com/v1/chat/completions"
data = {
"model": "generalv3.5", # 指定请求的模型
"messages": [
{
"role": "user",
"content": content_with_prompt # 使用函数参数替换content
}
],
"stream": False
}
header = {
"Authorization": "Bearer your_api_password", # 注意此处替换自己的APIPassword
"Content-Type": "application/json"
}
response = requests.post(url, headers=header, json=data)
content = parse_content(response.text)
return content # 返回响应的文本内容
大模型返回的结果内容较多,我们用comate写一个解析函数,只获取其中的某些必要字段,在comate输入
写一个函数解析下面json中的content字段:{ "code": 0, "message": "Success", "sid": "cha000b7045@dx1915dab7328b8f2532", "choices": [{ "message": { "role": "assistant", "content": "你好!有什么我可以帮忙的吗?" }, "index": 0 }], "usage": { "prompt_tokens": 1, "completion_tokens": 7, "total_tokens": 8 } }
得到函数:
def parse_content(json_data):
# 解析JSON字符串为Python字典
data = json.loads(json_data)
# 检查'choices'键是否存在,并且其值是否是一个列表
if 'choices' in data and isinstance(data['choices'], list):
# 遍历choices列表中的每个元素
for choice in data['choices']:
# 检查当前元素是否包含'message'键,并且'message'是一个字典
if 'message' in choice and isinstance(choice['message'], dict):
# 检查'message'字典中是否包含'content'键
if 'content' in choice['message']:
# 返回找到的第一个'content'字段的值
return choice['message']['content']
# 如果没有找到'content'字段,则返回None
return None
prompt管理器较为简单:
def get_prompt():
prompt = "假设你是一个五星大厨,掌握了很多做饭做菜的技巧,尤其擅长家常菜,请你根据提供的蔬菜、主食,提供一道比较合适的家常菜," \
"蔬菜、主食会用[]符号包裹起来,要求只能使用提供的菜,要求提供做的菜的名字以及做菜步骤,可以用1.xx,2.xx,3.xx的格式来提供"
return prompt
至此,服务端的函数基本已经完成。
服务的启动命令为:
python3 -m gunicorn -w 1 -b 0.0.0.0:6001 server:app
下面写前端页面:
前端不太了解,只能完全依靠comate了。这里的提示词没有记录太全,只能做大致的记录:
你需要生成一个前段页面,页面题目是:剩菜好帮手,需要包括一个对话框,用户可以输入文字,包括一个发送按钮,点击后可以发送请求个上面的服务端。
之后comate生成一个index.html,并提示出应该放置的位置。经过多次提示,又添加了清除答复的按钮,以及将答复框做成了自适应宽度,并调整了最大宽度等。
为了访问页面,服务端代码中需要插入下面的代码,这个comate也会提示
@app.route('/cook')
def index():
return render_template('index.html') # 这将从templates文件夹中提供index.html文件
效果:
感受
comate 能快速生成代码,大部分只要提示词准确,生成的代码不用修改或者少量修改就可以使用。在AI时代,阻碍人前进的不再是复杂的编程语言语法细节,而是人的创意想法,只要有idea,大模型、comate都可以快速低成本的完成实践