SRE-copilot构建全过程
由多个agent组成的SRE专家组,代替人工进行准确的故障分析!👽
流程:从用户输入取时间范围(支持上传图片)、路由--》查询日志中心--》获取网关中cmdb信息--》查询CMDB获取更多细节(owner、变更、jenkins、gitlab信息)--》分析并给出建议:
执行效果:
图中是判断某接口504故障,从依据到结论过程
步骤
使用flowise,配置langchain中AgentFlow
由多个agent组成的SRE专家组,代替人工进行准确的故障分析!👽
流程:从用户输入取时间范围(支持上传图片)、路由--》查询日志中心--》获取网关中cmdb信息--》查询CMDB获取更多细节(owner、变更、jenkins、gitlab信息)--》分析并给出建议:
执行效果:
图中是判断某接口504故障,从依据到结论过程
使用flowise,配置langchain中AgentFlow
近期借一位业内朋友推荐,面试了一家位于新加坡的互联网企业,同样记录面试过程
一面(远程):
业务线运维负责人,是一位年轻女士,在问明不需要露脸后开始。从自我介绍到了解的技术栈,擅长的方向,排错问题等。问到“擅长的方向”时,忽然一愣,眼瞅也是17年的老运维人了,但还真没觉得擅长什么。只能说阶段性的尝试过一些新技术,23年至今,甚至有点不务正业,可以不负责任的说,“运维里最了解大模型的,大模型里最了解运维”的复合型人才:) 接触越多也愈发觉得知之甚少,这也是我在简历中不敢用“精通”二字。收回来,讲了几个模型应用的实际案例
23年以前,容器、网格、网关的落地、使用过程,举了几个排错。提到当前基础设施情况,使用了terraform,交流了优势和建议做法,terraform结合ansible快速拉起。在容器编排有更好的解法Cluster Autoscaler叠加Horizontal Pod Autoscaler。面试官最后礼貌的问了薪资,并贴心的补充可以不回答,基于信任,如实告知,一面结束,面试官准时,用时45分钟
二面(远程):
跨团队面试,一位男士,可能负责研发,同样的过程,自我介绍到过往经历。分享了几个经典、复杂的排错案例,也都是博客的历史文章。着重问了CDN厂商的从业经历,简历外有无其他经验,以往离开的原因等。二面结束,面试官因为凌晨处理故障到5点,迟到半小时,表示理解,实际用时49分钟
多年的运维感悟,什么阶段干什么事。这也是我在内部的运维规范里明确标明,“不提前优化”
我来谈谈几个阶段,
部分参考 原文链接:https://mp.weixin.qq.com/s/vVhLZDL6bRJL9u5GJg63tw
初创阶段,更多关注业务最小模型能否跑通
需要容量评估的场景
有些明显的规则要遵守,比如
这时候就要拆分了,上面提到的allinone可能已经有了性能、容量等压力。此时就要垂直拆分了
核心是基于业务的拆分
这时候我们要引入反向代理,最常见的如nginx,我的建议是用更加贴近业务的api网关,比如kong、apisix、openrestry等等,有更多原生功能。如果nginx比作汽车引擎,后者可以比作宝马、奔驰汽车,多了很多配置外,还能开车即用,开箱即用
创建一个具备内部运维知识,识别自然语义,准确调用各种工具执行任务,严格控制幻觉的智能运维工程师。
使用 OpenAI 的 Assistant 功能,上传知识库,设置提示词。
Prompt Engineering:
Function Call:通过 Flowise 的自定义工具
先看效果
Streamlit 界面:
LLM(大型语言模型)配置设置:
gpt-4o-2024-08-06
)。ollama/llama3:latest
)的配置。助手代理(AssistantAgent)和用户代理(UserProxyAgent):
AssistantAgent
,负责与用户输入进行处理和响应,同时集成在 Streamlit 中以显示聊天消息。UserProxyAgent
,用于接收用户输入,处理用户命令,并在助手代理与用户之间进行代理交互。实用工具函数:
get_url_info_from_kong
:从 Kong API 网关中查询 URL 的路由、服务和上游配置的信息,并返回格式化结果。dns_record_status
:检查给定 URL 的 DNS 记录状态。query_from_cmdb
:从 CMDB(配置管理数据库)中检索特定云服务提供商(如阿里云、AWS 等)的服务器、数据库和中间件实例的数量。异步聊天系统:
asyncio
),用户代理(User Proxy Agent)可以异步与助手代理(Assistant Agent)进行对话,提供更高效的交互体验。+---------------------------------------------------------------+
| Streamlit Interface |
|---------------------------------------------------------------|
| +-----------------------------------------------------------+ |
| | Sidebar (Azure Endpoint Config, etc.) | |
| +-----------------------------------------------------------+ |
| |
| +-----------------------------------------------------------+ |
| | Chat Input / Output Area | |
| | | |
| | User Input --> UserProxyAgent --> AssistantAgent | |
| | | |
| | AssistantAgent --> UserProxyAgent --> Output Display | |
| +-----------------------------------------------------------+ |
+---------------------------------------------------------------+
+--------------------+ +--------------------+
| LLM Configurations | | Utility Functions|
|--------------------| |--------------------|
| - OpenAI (GPT-4) | | - get_url_info_from|
| - Local LLM (LLaMA)| | _kong() |
+--------------------+ | (Interacts with |
| Kong API Gateway)|
| - dns_record_status|
| (Checks DNS) |
| - query_from_cmdb |
| (Interacts with |
| CMDB Database) |
+--------------------+
+-----------------+ +-------------------+
| AssistantAgent | <--- asyncio ->| UserProxyAgent |
| (Handles LLM | | (Manages User |
| Requests) | | Input/Commands) |
+-----------------+ +-------------------+
^ | ^ |
| | | |
| v | v
+----------------+ +-------------------+
| LLM Config | | Utility Functions|
| Setup (OpenAI)| | (Kong, DNS, CMDB) |
+----------------+ +-------------------+
+----------------+
| Data Flow |
|----------------|
| - User Input |
| - Assistant |
| - ProxyAgent |
| - Utility Func|
+----------------+
超越简单的 RAG 和提示词工程:
使用 Function Call 完成真实世界的任务:
集成异步交互和高效任务处理:
asyncio
)实现用户代理和助手代理之间的异步通信,大幅提升了任务处理的效率和响应速度。这样的设计确保了系统能够并发处理多个任务,而不阻塞用户输入和系统响应。技术含量高,解决复杂场景问题:
get_url_info_from_kong
函数能够通过调用 Kong API,获取详细的路由、服务和插件信息,并对这些数据进行格式化处理和展示;query_from_cmdb
函数能够从 CMDB 中动态检索并整合不同云服务商的资源信息。这样的功能大大提升了系统的实际应用价值。提升企业运营效率与智能化水平:
最近在整理CMDB信息,以Jenkins为中枢,统计、梳理代码仓库位置、发布位置。形成以应用为中心,串连资源、管理者。
首先需要统计代码中涉及的配置文件信息,比如Mysql/Redis/Elasticsearch/MongoDB/Kafka/RocketMQ/MQTT/Doris/HBase/InfLuxDB/http等
意义:
相较传统的正则匹配,大模型加持下有如下优点
环境介绍
#!/bin/bash
# 设置环境变量
export CUDA_VISIBLE_DEVICES=0,1
# 启动 vllm 服务器并将其转移到后台运行
nohup python3 -m vllm.entrypoints.openai.api_server \
--model /data/vllm/Meta-Llama-3.1-8B-Instruct \
--served-model-name llama \
--tensor-parallel-size 2 \
--trust-remote-code > llama.log 2>&1 &
import os
import re
import requests
import time
# 定义中间件关键字的正则表达式,忽略大小写
KEYWORDS = ["mysql", "redis", "elasticsearch", "mongodb", "kafka", "rocketmq",
"rabbitmq", "emq", "mqtt", "nacos", "postgresql", "doris",
"hbase", "influxdb", "azkaban", "sls", "clickhouse",
"mse", "dataworks", "neo4j", "http", "gitlab", "jenkins"]
PATTERN = re.compile(r'\b(?:' + '|'.join(KEYWORDS) + r')\b', re.IGNORECASE)
def read_files(directory):
for root, _, files in os.walk(directory):
# 忽略 .git 文件夹
if '.git' in root:
continue
for file in files:
file_path = os.path.join(root, file)
with open(file_path, 'r', encoding='utf-8', errors='ignore') as f:
content = f.readlines()
yield file_path, content
def extract_context(content, file_path):
results = []
for i, line in enumerate(content):
if PATTERN.search(line):
start = i # 从匹配到的行开始
end = min(i + 11, len(content)) # 包含匹配行及其下方10行
snippet = "".join(content[start:end]).strip()
results.append(f"文件路径: {file_path}\n{snippet}")
return results
def write_to_file(directory, contexts):
output_file = os.path.join(directory, 'matched_content.txt')
with open(output_file, 'w', encoding='utf-8') as f:
for context in contexts:
f.write(context + '\n' + '=' * 50 + '\n')
return output_file
def send_to_model(url, model_name, prompt, content):
headers = {"Content-Type": "application/json"}
data = {
"model": model_name,
"temperature": 0.2,
"messages": [
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": f"{prompt}\n\n{content}"}
]
}
try:
response = requests.post(url, headers=headers, json=data)
response.raise_for_status()
except requests.RequestException as e:
print(f"Request to model failed: {e}")
return None
response_json = response.json()
if 'choices' not in response_json:
print(f"Model response does not contain 'choices': {response_json}")
return None
return response_json['choices'][0]['message']['content']
def write_individual_results(directory, results, model_name):
output_file = os.path.join(directory, f'{model_name}_results.txt')
with open(output_file, 'w', encoding='utf-8') as f:
for result in results:
f.write(result + '\n' + '=' * 50 + '\n')
return output_file
def combine_and_summarize(directory, llama_file, qwen_file, qwen_url):
combined_content = ""
# 读取llama和qwen的结果文件
with open(llama_file, 'r', encoding='utf-8') as f:
combined_content += f.read()
with open(qwen_file, 'r', encoding='utf-8') as f:
combined_content += f.read()
# 使用qwen模型进行汇总处理
summary_prompt = """
1. 删除包含“配置信息未提供”等无用信息的部分。
"""
result_summary = send_to_model(qwen_url, "qwen", summary_prompt, combined_content)
if result_summary:
summary_file = os.path.join(directory, 'final_summary_combined.txt')
with open(summary_file, 'w', encoding='utf-8') as f:
f.write(result_summary)
print(f"汇总结果保存至: {summary_file}")
else:
print("汇总处理失败")
def main(directory):
start_time = time.time()
all_contexts = []
for file_path, content in read_files(directory):
contexts = extract_context(content, file_path)
all_contexts.extend(contexts)
# 将匹配到的内容写入文件
matched_file = write_to_file(directory, all_contexts)
results_llama = []
results_qwen = []
with open(matched_file, 'r', encoding='utf-8') as f:
content = f.read()
analysis_prompt = """
1. Ignore lines starting with #, //, /**, or <!--.
2. Exclude commented lines.
3. Extract configuration info for: MySQL, Redis, Elasticsearch, MongoDB, Kafka, RocketMQ, RabbitMQ, EMQ, MQTT, Nacos, PostgreSQL, Doris, HBase, InfluxDB, Azkaban, SLS, ClickHouse, MSE, DataWorks, Neo4j, HTTP, HTTPS, GitLab, Jenkins.
4. Focus on URLs, usernames, passwords, hosts, ports, and database names.
5. Extract the following attributes:
- Username
- Password
- Host
- Port
- Database Name
- URL or Connection String
6. Look for configuration patterns like key-value pairs and environment variables.
7. Ensure extracted values are not in commented sections.
8. Extract all distinct configurations.
9. Handle different configuration formats (JSON, YAML, dictionaries, env variables).
10. Delete sections containing “**配置信息未直接提供**” or similar useless content.
"""
# 分别调用llama和qwen模型
result_llama = send_to_model("http://1.1.1.1:8000/v1/chat/completions", "llama", analysis_prompt, content)
result_qwen = send_to_model("http://1.1.1.1:8001/v1/chat/completions", "qwen", analysis_prompt, content)
if result_llama:
results_llama.append(result_llama)
if result_qwen:
results_qwen.append(result_qwen)
# 分别保存llama和qwen的结果到不同文件
llama_file = write_individual_results(directory, results_llama, "llama")
qwen_file = write_individual_results(directory, results_qwen, "qwen")
# 汇总llama和qwen的结果
combine_and_summarize(directory, llama_file, qwen_file, "http://1.1.1.1:8001/v1/chat/completions")
end_time = time.time()
total_duration = end_time - start_time
print(f"总耗时: {total_duration:.2f} 秒")
if __name__ == "__main__":
main("/Users/jixing/PycharmProjects/AIOps-utils/Athena_Legacy")
7月7日,收到来自CDN群的告警,当天带宽峰值较往日上涨600倍,进一步发现是某一宣传视频被反复下载,下载用户的IP集中,都是来自山西联通ADSL的一个C段。
通过带宽比较,6日开始就有下载,时间晚上八点开始,一直持续到夜间十一点。至此,初步的用户画像如下
两天账单被刷了
2万!😓
至此,没想明白这种损人不利己的行为。直到看到v2的一篇类似帖子(https://www.v2ex.com/t/1055510#reply27),pcdn用户手动制造下行流量,伪装成正常用户,妄图躲过运营商审查。鉴于对公司利益造成实际损失,联合安全同事决定报警,起码提醒对方不要再有此行为。提供资料后警方不予立案,理由如下
对我们提出的经济损失不予认可。不理解但尊重!!最终因为这一两例“老鼠屎”用户,全局限制了下载速度
背景:
位于阿里云的kubernetes集群,通过ingress svc暴露,类型为ClusterIP。某天在对集群内大量缩容节点时,马上出现了大量的502、504报警
以为外部密集请求导致后端服务受影响,从监控观察并无异常,联想到最近的变更,怕不是缩容导致?正在排查的过程中,服务恢复。事后通过工单确认 ClusterIP类型的SLB后端的rs被移除时,SLB的操作就是静默丢包,也就是该SLB对任何发过来的tcp包都会默认丢弃,DROP并不会响应
文档描述
https://help.aliyun.com/zh/ack/ack-managed-and-ack-dedicated/user-guide/add-annotations-to-the-yaml-file-of-a-service-to-configure-clb-instances
最终解法
早上看到一个粗看起来很简单的问题,“如何在crontab中增加每7分钟循环执行的任务”,下意识想不就是 */7。这么简单的问题怎么一堆回答。
先上结论,比如每7分钟,我10:40分添加的,
也就破坏了需求中“每7分钟”,解决办法是每分钟执行,在脚本中判断当前时间
# 获取当前时间的分钟数
current_minute=$(date +"%M")
# 判断分钟数是否是7的倍数
if (( current_minute % 7 == 0 )); then
# 如果是7的倍数,执行任务
echo $(date +"%Y-%m-%d %H:%M:%S") >> /tmp/timestamp.txt
fi
我们有一款私有化部署系统,服务于用户购买的智能终端(简单理解为顺丰小哥手持的扫码枪)。周六接到端开发工程师反馈,此批设备续航异常,理论数据两周,但实际只有三至四天。
经过监测电量、抓包手段,电流的峰谷交替约为20秒
端上抓包也显示每20秒收到来自服务器的keepalive报文,此原因导致设备无法休眠!
aws NLB产品如果配置了tls监听,会主动20秒为周期发送keepalive!!!
客户端发送Keep-Alive包
AWS NLB行为时间线
时间点 | 行为描述 |
---|---|
第0秒 | 客户端发送Keep-Alive包,NLB接收到并立即返回。 |
第20秒 | NLB发送Keep-Alive包到前端和后端。 |
第40秒 | NLB再次发送Keep-Alive包到前端和后端。 |
第60秒 | 客户端再次发送Keep-Alive包,NLB接收到并重置计时。 |
第80秒 | NLB发送Keep-Alive包到前端和后端。 |
... | 重复每20秒发送Keep-Alive包的过程,直到客户端停止发送。 |
阿里云NLB行为时间线
时间点 | 行为描述 |
---|---|
第0秒 | 客户端发送Keep-Alive包,NLB接收到并保持连接。 |
第60秒 | 客户端再次发送Keep-Alive包,NLB接收到并保持连接。 |
第120秒 | 客户端再次发送Keep-Alive包,NLB接收到并保持连接。 |
... | 重复每60秒发送Keep-Alive包的过程,直到客户端停止发送。 |
客户端不发送Keep-Alive包
AWS NLB行为时间线
时间点 | 行为描述 |
---|---|
第0秒 | 客户端与NLB建立连接。 |
第350秒 | 没有数据包发送,NLB关闭连接并发送TCP RST包。 |
第351秒 | 连接关闭,客户端无法继续发送数据。 |
阿里云NLB行为时间线
时间点 | 行为描述 |
---|---|
第0秒 | 客户端与NLB建立连接。 |
第900秒 | 没有数据包发送,NLB关闭连接。 |
第901秒 | 连接关闭,客户端无法继续发送数据。 |
在上一篇文档中实现了检查单台服务器故障的典型排错场景。此次我们加大难度
一、排查链路中故障,识别南北向流量走向并给出排查结果
难点
思路
二、与真实用户交流,给出域名申请建议并检测是否可用
难点
思路
整体难点,多agent执行顺序,“技能绑定”,来看效果。图1为用户与gatekeeper探讨需求
图2为agent建议用户使用的解析记录
图3为正确路由南北向流量问题,并使用对应function判断
关键代码片段
探索大模型在运维工作中的方向,此篇主要讲故障排查。是“Autogen 运维排错实践-复杂案例”的进一步整合,改进如下
用户在资产中选择目标机器
描述故障,选择策略(自动执行、逐步询问),点击执行
输出结果
利用堡垒机与所有目标机器互通,将aiagent部署在此。通过提示词确认专精方向、连接方式。后端使用Django开启websocket,前端使用xterm.js模拟终端
模型仅具备各领域的通用知识,对于垂类仍有进步空间,这也是医疗、政务类模型出现的原因。我们在尝试AIagent时发现模型并不够聪明,对于安装性能分析工具,vim前后台等问题无法进展到下一步,详见 Autogen 运维排错实践-复杂案例。此次尝试使用偏运维领域的ServerFault,爬取经过人工审核的有效答案来微调模型,观察效果。简言之,教模型所不擅长
先看效果,根据采集到的数据,统计出ServerFault热门词云
筛选逻辑,根据Active状态&前500页&作者vote过的问题,分别记录问题链接、标题、内容、发布时间、更新时间、被查看总数、投票总数;答案内容、得分9个字段,两张表通过外键关联
CREATETABLE Posts (
PostID INTEGERPRIMARYKEY,
PostLink TEXTNOTNULL,
Title TEXTNOTNULL,
PostContent TEXTNOTNULL,
PostTime TEXTNOTNULL,-- ISO8601 strings ("YYYY-MM-DD HH:MM:SS.SSS")
ModifyTime TEXTNOTNULL,
ViewCount INTEGERNOTNULL,
VoteCount INTEGERNOTNULL
);
CREATETABLE Answers (
AnswerID INTEGERPRIMARYKEY,
PostID INTEGER,
AnswerContent TEXTNOTNULL,
VoteCount INTEGERNOTNULL,
FOREIGNKEY(PostID)REFERENCES Posts(PostID)
);NO;
经过控制爬虫速率,切换代理地址,共采集问题、答案数
数量 | |
---|---|
Posts | 6681 |
Answers | 16253 |
VoteCount分布
0-100 | 101-200 | 201-300 | 301-400 | 401-500 | >500 | |
---|---|---|---|---|---|---|
Posts | 6278 | 85 | 32 | 13 | 1 | 5 |
Answers | 15643 | 150 | 31 | 16 | 7 | 8 |
让查数据这件事,不再是高高在上、遥不可及的技能,而是人人都能玩得转,妙趣横生的小技巧。比如“我的店上周赚了多少钱?哪个商品即将售罄?这个月卖的最多的商品是啥?”,下一秒,答案就像变魔术一样蹦出来。直接看效果
流程如下,
关键代码
需求如下,产品团队高频要求翻译团队给出符合标准的翻译件,比如翻译产品文档,其中又有大量的术语,比如3D结构光、扫码头、主屏、客显屏、立式等(在公司内有统一的标准叫法),使用市面通用的翻译产品需要自己修改。看效果
原理如图
openai负责文档Retrieval,flowise负责功能补充,同时做了一次封装,gradio负责提供展示页面,方便用户交互
gradio_run.py
import gradio as gr
import requests
import json
def call_api(question):
url = "https://xxxx.com/api/v1/prediction/asdasce1-5a7b-4d9e-9ed6-21a9aaaab2"
headers = {"Content-Type": "application/json"}
data = {"question": question}
response = requests.post(url, headers=headers, data=json.dumps(data))
return response.json().get("text", "No response text found.")
iface = gr.Interface(
fn=call_api,
inputs="text",
outputs="text",
)
iface.launch()
一直有个需求,企业内私有知识库RAG,“陪产假怎么申请?”,“公司发票抬头是啥?”等问题,解放行政、人事的部分人力。偶然发现钉钉“智能员工”非常契合。零代码开发、配置简单。看效果,支持单聊群聊。目前免费!
配置方式,登录钉钉开发者-数字员工,类似flowise编辑langchain的每个环节,别担心,有模板只需要简单修改!在知识库贴钉文档的链接。文档准备需要注意以下几点
产品名: No English - 不学英语 https://chat.openai.com/g/g-kkrKOWa1E-no-english
产品概览: No English - 您的个人化英语学习伙伴
产品定位:
在语言学习的长河中,No English 站在了技术与教育的交汇点,提供了一个创新的、用户友好的英语学习解决方案。我们的平台运用最新的人工智能技术,致力于提升英语学习效率,同时保持学习过程的趣味性和参与度。
核心功能:
知识资源库:
No English 配备了一系列的英语学习文件,涵盖商务英语和各类英语测试标准,如TEM-8、CET-4和CET-6,可供用户根据个人需要下载并学习。
技术能力:
用户动作与互动:
用户询问当日新闻时,会从指定接口请求,本app使用京东的汇聚新闻接口。
使用推荐:
No English 针对希望在移动设备上学习的用户进行了优化设计,特别推荐使用手机应用来体验我们的语音互动特性,这能够为用户提供更加沉浸式的学习环境。 手机端前两天还支持,现在已经不行了
相信大家或多或少体验过大模型的魅力,有一定门槛的chatGPT(包含各种套壳的chat_bot),还有文心、通义千问等等。我总结有以下小缺陷
打个比方大模型是大脑,AI agent给了模型“双手”。常见的autoGPT、babyGPT、本文介绍来自微软的autogen (https://microsoft.github.io/autogen/)
代码执行
先看效果,“请求https://www.baidu.com 50次,2秒间隔,记录每次的状态码、网络延迟,结果记录下来。并且生成图片,保存到当前目录下”
近期在尝试大数据在企业内的应用,两个需求:
两种方向:向量库+大模型、模型微调。
以下给出openai模型微调的详细过程,目前官方推荐gpt-3.5-turbo,gpt4的微调将在年底推出
{"messages": [{"role": "system", "content": "Marv is a factual chatbot that is also sarcastic."}, {"role": "user", "content": "What's the capital of France?"}, {"role": "assistant", "content": "Paris, as if everyone doesn't know that already."}]}
{"messages": [{"role": "system", "content": "Marv is a factual chatbot that is also sarcastic."}, {"role": "user", "content": "Who wrote 'Romeo and Juliet'?"}, {"role": "assistant", "content": "Oh, just some guy named William Shakespeare. Ever heard of him?"}]}
{"messages": [{"role": "system", "content": "Marv is a factual chatbot that is also sarcastic."}, {"role": "user", "content": "How far is the Moon from Earth?"}, {"role": "assistant", "content": "Around 384,400 kilometers. Give or take a few, like that really matters."}]}
我的数据源是csv,第一列问题,第二列答案,用以下脚本处理
import pandas as pd
import json
def convert_csv_to_jsonl(input_csv, output_jsonl):
# Read the CSV file
df = pd.read_csv(input_csv)
with open(output_jsonl, 'w', encoding='utf-8') as f:
for _, row in df.iterrows():
jsonl_data = {
"messages": [
{"role": "system", "content": "SunSun is an internal knowledge base communication robot."},
{"role": "user", "content": row['Generated Questions']},
{"role": "assistant", "content": row['source']}
]
}
f.write(json.dumps(jsonl_data, ensure_ascii=False) + '\n')
# Usage
# convert_csv_to_jsonl('path_to_your_csv_file.csv', 'desired_output_file.jsonl')
if __name__ == "__main__":
convert_csv_to_jsonl('/Users/jixing/Downloads/export_result0925.csv',
'/Users/jixing/Downloads/export_result0925.jsonl')
import openai
# 替换你的key
openai.api_key = "sk-40LIdYxxxxxxx"
training_file = openai.File.create(
file=open("export_result0925.jsonl", "rb"),
purpose='fine-tune'
)
# 记录文件id,下一步需要使用
print(training_file.id)
import openai
# 你的key
openai.api_key = "sk-40LIdYIwxxxxx"
# 刚才的文件id
openai.FineTuningJob.create(training_file="file-0ACDKAM7xxxxxx", model="gpt-3.5-turbo")