最近在整理CMDB信息,以Jenkins为中枢,统计、梳理代码仓库位置、发布位置。形成以应用为中心,串连资源、管理者。
首先需要统计代码中涉及的配置文件信息,比如Mysql/Redis/Elasticsearch/MongoDB/Kafka/RocketMQ/MQTT/Doris/HBase/InfLuxDB/http等
意义:

  • 排错时参考,比如该服务报504错误,马上定位到具体的中间件或者外部接口
  • 不遵守配置规范,不用配置中心硬编码在代码中的情况,能被发现
  • 资源收拢,当所有代码中都未出现的资源可作为下架依据

相较传统的正则匹配,大模型加持下有如下优点

  • 大模型能够识别到更多的配置,正则更依赖规则,对于语言多、规范复杂、执行不严有优势
  • 大模型能够用简单的提示词,格式化、筛选输出

环境介绍

  • 效果展示,使用了内部CMDB项目代码,使用了Python的Django框架,代码大小5M。项目中的配置相对分散。分析过程30秒。

效果.jpg

  • 代码、配置资产属于机密信息,故使用近期评分较高的本地模型确保安全性,Meta-Llama-3.1-8B-Instruct/Qwen2-7B-Instruct,使用vllm部署
#!/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 &
  • 逻辑如下,通过多个模型分别判断,取并集后再利用模型整合

20240812163027.jpg

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段。20240807163117.jpg
通过带宽比较,6日开始就有下载,时间晚上八点开始,一直持续到夜间十一点。至此,初步的用户画像如下

  • 恶意用户IP集中,都是来自山西联通adsl用户。221.205.169段
  • 时间点固定20:00-23:00
  • 下载大文件,我们是一段100MB的宣传视频。每3-5秒下载一次
  • 屏蔽IP后,会更换IP,不断变化UA进行下载

两天账单被刷了

2万!😓

至此,没想明白这种损人不利己的行为。直到看到v2的一篇类似帖子(https://www.v2ex.com/t/1055510#reply27),pcdn用户手动制造下行流量,伪装成正常用户,妄图躲过运营商审查。
20240807160710.jpg

鉴于对公司利益造成实际损失,联合安全同事决定报警,起码提醒对方不要再有此行为。提供资料后警方不予立案,理由如下

  • 宣传视频本属于公开资源
  • 源站当时未宕机

对我们提出的经济损失不予认可。不理解但尊重!!最终因为这一两例“老鼠屎”用户,全局限制了下载速度

背景:
位于阿里云的kubernetes集群,通过ingress svc暴露,类型为ClusterIP。某天在对集群内大量缩容节点时,马上出现了大量的502、504报警
500告警.jpg

以为外部密集请求导致后端服务受影响,从监控观察并无异常,联想到最近的变更,怕不是缩容导致?正在排查的过程中,服务恢复。事后通过工单确认
ClusterIP类型的SLB后端的rs被移除时,SLB的操作就是静默丢包,也就是该SLB对任何发过来的tcp包都会默认丢弃,DROP并不会响应

  • 如果此时此链接是空闲链接,就会等待SLB设置的空闲链接超时时间,超过这个时间后,SLB断开链接
  • 如果此时此链接还有请求,由于SLB不会回ack包,客户端就会超时重传,重传的时间就取决于客户端或者内核的超时设置,比如内核ack默认会进行15次重传(net.ipv4.tcp_retries2=15),累加起来大概是924秒,JDBC URL支持设置SocketTimeout、ConnectTimeout,会在这个超时时间后断开重连

文档描述
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
注解.jpg

最终解法

  • 代码中判断,失败重新建立连接
  • 在ingress中开启上述注解,缩短时间。并在谷值时缩容

早上看到一个粗看起来很简单的问题,“如何在crontab中增加每7分钟循环执行的任务”,下意识想不就是 */7。这么简单的问题怎么一堆回答。
先上结论,比如每7分钟,我10:40分添加的,

  • 第一次执行是在10:42,而不是下意识的10:47
  • 10:56最后一次执行,下一次执行是在11:00,而不是想象中的11:03

cron.jpg
也就破坏了需求中“每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秒电量消耗.jpg
端上抓包也显示每20秒收到来自服务器的keepalive报文,此原因导致设备无法休眠!

排查过程:

  1. 首先去服务端抓包,很清晰也能看到每20秒。当我看到抓包结果时,马上自信的答复,这是端上发来的keepalive包呀,肯定端上问题,不查端查服务器干嘛- -!keepalive包.jpg
  2. battle,端开发工程师回怼,“我抓包是从服务器端返回的”,仔细一看还真是。这时候懵圈,证据显示都是对方发来的,且正确响应
  3. 没头绪时,在想经历的中间环节。智能终端---aws NLB---mqtt服务器,NLB配置了标准的tls,卸载证书后转发到mqtt,功能测试都正常,且NLB用了这么久,不应该有问题
  4. 难道是运营商或者网络设备发送?想想不可能,tcp无法伪造IP,持续没头绪
  5. 中间环节只有NLB,进一步排查发现aws的NLB有区别于其他云的独特“机制”aws机制.jpg
  6. 接下来就是验证EIP直接暴露,问题消失。问题确认

总结:

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秒连接关闭,客户端无法继续发送数据。

在上一篇文档中实现了检查单台服务器故障的典型排错场景。此次我们加大难度
一、排查链路中故障,识别南北向流量走向并给出排查结果

  • 难点

    • 模型无法理解私有环境
    • 给出的建议具备安全风险
  • 思路

    • 结合提示词,使用function call功能来准确获取配置
    • 新增the_supervisor角色,对planer的建议安全过滤

二、与真实用户交流,给出域名申请建议并检测是否可用

  • 难点

    • 引导用户使用合规的子域名
  • 思路

    • 提示词约定规范,使用socket库来判断可用性

整体难点,多agent执行顺序,“技能绑定”,来看效果。图1为用户与gatekeeper探讨需求pic1.jpg
图2为agent建议用户使用的解析记录pic2.jpg
图3为正确路由南北向流量问题,并使用对应function判断pic3.jpg

关键代码片段
pic4.jpg

探索大模型在运维工作中的方向,此篇主要讲故障排查。是“Autogen 运维排错实践-复杂案例”的进一步整合,改进如下

  • 通过跳板机,不需要在目标机器安装agent,零侵入
  • 入口统一,集成在运维系统
  • 模型自由切换,GPT-4/Claude/tongyi等等

效果

用户在资产中选择目标机器
host_management.png
描述故障,选择策略(自动执行、逐步询问),点击执行
start_tr.png
输出结果
end_tr.png

方案设计

利用堡垒机与所有目标机器互通,将aiagent部署在此。通过提示词确认专精方向、连接方式。后端使用Django开启websocket,前端使用xterm.js模拟终端
topology.png

重点

  • Xterm.js学习曲线陡峭,捕获中文、英文、空格、回退,快捷键等均需要自定义。在即将完成时看到有封装更简单的项目webssh
  • Autogen中与openai通信使用了api.openai.com,改对应库中的域名至代理域名
  • websocket模式需要配置asgi使用,加载静态文件有差别。consumers.py和routing.py需自定义
  • AIagent中提示词需要明确,注意模型的上下文限制,通过提示词截取部分结果

- 阅读剩余部分 -

模型仅具备各领域的通用知识,对于垂类仍有进步空间,这也是医疗、政务类模型出现的原因。我们在尝试AIagent时发现模型并不够聪明,对于安装性能分析工具,vim前后台等问题无法进展到下一步,详见 Autogen 运维排错实践-复杂案例。此次尝试使用偏运维领域的ServerFault,爬取经过人工审核的有效答案来微调模型,观察效果。简言之,教模型所不擅长

步骤

  1. 爬取ServerFault,筛选有效问答
  2. 微调模型
  3. AutoGen配置微调后模型

先看效果,根据采集到的数据,统计出ServerFault热门词云
ciyun-new.jpg

爬虫

筛选逻辑,根据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;

经过控制爬虫速率,切换代理地址,共采集问题、答案数

数量
Posts6681
Answers16253

VoteCount分布

0-100101-200201-300301-400401-500>500
Posts627885321315
Answers15643150311678

- 阅读剩余部分 -

让查数据这件事,不再是高高在上、遥不可及的技能,而是人人都能玩得转,妙趣横生的小技巧。比如“我的店上周赚了多少钱?哪个商品即将售罄?这个月卖的最多的商品是啥?”,下一秒,答案就像变魔术一样蹦出来。直接看效果

流程如下,

  • 前端使用gradio,采集麦克风声音
  • 后端收到声音,使用openai的speech-to-text
  • 文字通过dbgpt(链接各种自定义模型),转换为sql进行数据库查询
  • 返回对应数据

关键代码

- 阅读剩余部分 -

需求如下,产品团队高频要求翻译团队给出符合标准的翻译件,比如翻译产品文档,其中又有大量的术语,比如3D结构光、扫码头、主屏、客显屏、立式等(在公司内有统一的标准叫法),使用市面通用的翻译产品需要自己修改。看效果
翻译效果.jpg

原理如图
Retrieval.png

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,“陪产假怎么申请?”,“公司发票抬头是啥?”等问题,解放行政、人事的部分人力。偶然发现钉钉“智能员工”非常契合。零代码开发、配置简单。看效果,支持单聊群聊。目前免费!
RAG-01.jpg
RAG-02.jpg

配置方式,登录钉钉开发者-数字员工,类似flowise编辑langchain的每个环节,别担心,有模板只需要简单修改!在知识库贴钉文档的链接。文档准备需要注意以下几点

  1. 主题、内容紧密相关
  2. 段落清晰,一段文字不超过500字,长文本可以拆成多段
  3. QA优先级最高
  4. 用标准中文,反对“互联网黑话”
  5. 单文件不超10mb
  6. 通义模型基于中文,中文提示词效果更好
  7. 温度尽可能低,0.1

钉钉AI.jpg

产品名: No English - 不学英语 https://chat.openai.com/g/g-kkrKOWa1E-no-english

产品概览: No English - 您的个人化英语学习伙伴

产品定位:
在语言学习的长河中,No English 站在了技术与教育的交汇点,提供了一个创新的、用户友好的英语学习解决方案。我们的平台运用最新的人工智能技术,致力于提升英语学习效率,同时保持学习过程的趣味性和参与度。

核心功能:

  1. 每日单词学习: 我们的系统每天精选实用词汇,配以例句和上下文,确保用户不仅记住单词,而且理解其应用。
  2. 定制化复习计划: 基于先进的记忆算法,No English 会提供个性化的复习计划,帮助用户巩固记忆,减少遗忘。
  3. 每日英语句型: 别名:"每天学点装比词汇😄 ",从日常对话到商务沟通,我们提供广泛的句型训练,以强化用户的实际应用能力。
  4. 学习动力激励: 别名:“今天踏马不想学了😕”,针对学习疲劳的用户,No English 提供及时的正能量和学习建议,激发学习激情。

知识资源库:
No English 配备了一系列的英语学习文件,涵盖商务英语和各类英语测试标准,如TEM-8、CET-4和CET-6,可供用户根据个人需要下载并学习。

技术能力:

  • 网页浏览: 无缝接入互联网资源,支持用户在学习中实时查询和获取信息。
  • DALL·E图像生成: 利用尖端的图像生成技术,为学习内容添加视觉元素,提高记忆点。
  • 代码解释器: 高级代码解释功能,为用户提供编程学习中的实时反馈。

用户动作与互动:
用户询问当日新闻时,会从指定接口请求,本app使用京东的汇聚新闻接口。

使用推荐:
No English 针对希望在移动设备上学习的用户进行了优化设计,特别推荐使用手机应用来体验我们的语音互动特性,这能够为用户提供更加沉浸式的学习环境。 手机端前两天还支持,现在已经不行了

app.jpg
app2.jpg

  1. GPT-4 Turbo模型登场,将上下文长度提升至128k,知识库更新到2023年4月!
  2. function call 提供线性调用
  3. 新增“seed” parameter,确保模型每次返回固定答案!
  4. 原生支持基于文档的知识“投喂”
  5. TTS中的所有音色都提供API
  6. 多模态,同一会话中集成dalle、Advanced Data Analysis、插件等
  7. GPT4支持微调,响应更快,费用更低
  8. 预示AI将能执行愈加复杂的任务,全民“技术平权”的时代到来

相信大家或多或少体验过大模型的魅力,有一定门槛的chatGPT(包含各种套壳的chat_bot),还有文心、通义千问等等。我总结有以下小缺陷

  1. 知识库有截止时间,比如GPT当前在21年9月
  2. 生成代码场景需要在本地手动执行、验证,反复贴报错最终得到一份可用的代码
  3. 无法理解私域任务,比如你们公司每天要做服务器安全巡检
  4. 准确度,在一些计算场景会乱答

打个比方大模型是大脑,AI agent给了模型“双手”。常见的autoGPT、babyGPT、本文介绍来自微软的autogen (https://microsoft.github.io/autogen/)
autogen官网.jpg

代码执行
先看效果,“请求https://www.baidu.com 50次,2秒间隔,记录每次的状态码、网络延迟,结果记录下来。并且生成图片,保存到当前目录下”

- 阅读剩余部分 -

近期在尝试大数据在企业内的应用,两个需求:

  • 用户输入自然语言后返回代码模版,最好能够进一步推理
  • 用户输入自然语言返回给定答案,不要扩展

两种方向:向量库+大模型、模型微调。
方向选择.jpg

以下给出openai模型微调的详细过程,目前官方推荐gpt-3.5-turbo,gpt4的微调将在年底推出

  • 数据预处理:准备至少10条数据,质量越高且数量越多,效果越好。如果没有就人工老老实实的标记几十条高质量数据,比大量低质数据更好。格式如下
{"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')
  • 上传文件至openai
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")

    最近在看大模型和运维行业的关联,初步想法是标记监控数据,配合混沌工程,给出故障数据进行多元线性回归,根据最佳曲线来预测故障。实际进行过程中发现困难重重,还在尝试标记数据。
    最近有个很火的词儿叫“数字孪生”,又叫数字骨灰盒:),大意是通过大量的文字痕迹训练已有模型,让模型从“扮演”到“重塑”你。受启发于 https://greatdk.com/1908.html,并做了些许优化,效果还是挺好玩的,或许这才是数字世界的你?!😄

看疗效
微调前1.jpg微调前2.jpg!微调.jpg
思路步骤:

  • 使用wechatExporter导出微信聊天记录,纯文本格式
  • 手动挑选适合训练的数据,对聊天记录众多的群聊进行排除
  • 自动数据清洗,合并聊天记录,记录历史
  • 使用ChatGLM2进行微调、推演
  • 启动web_demo就可以体验了😄

优化项目:

  • 兼容一问一答外,大多数人的聊天习惯是连续发出多条信息,当然我们回消息也可能是多条。比如张三问,1明天有空没? 2我想找你喝点 3别带媳妇,我回复:1有呀 2必须喝白的 3当然不带 4哈哈哈。我做了合并最终效果,
    {"prompt": "明天有空没?,我想找你喝点,别带媳妇", "response": "有呀,必须喝白的,当然不带,哈哈哈", "history": []}
  • 保留历史会话,沟通都是有上下文的,我这里简单粗暴的认为当天的会话都有关联,记录在history中
    {"prompt": "我去找你?", "response": "你开车了没", "history": [["?", "?"], ["忙完了", "怎么说"], ["吃饭打台球?", "行"]]}

python清洗脚本

import os
import re
import json

# 定义源文件夹和目标文件
source_folder = '/Users/jixing/Downloads/wechat_history'
output_file_path = '/Users/jixing/Downloads/0811output.txt'


# Regular expression patterns for extracting dates, usernames, and messages
date_pattern = re.compile(r"\((\d{4}-\d{2}-\d{2}) \d{2}:\d{2}:\d{2}\)")
user_msg_pattern = re.compile(r"^(.+?) \(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\):(.+)$")

# 遍历文件夹中的所有.txt文件
for filename in os.listdir(source_folder):
    if filename.endswith('.txt'):
        with open(os.path.join(source_folder, filename), 'r', encoding='utf-8') as source_file:
            content = source_file.readlines()

            # Parsing the chat data
            conversations = []
            current_date = None
            current_convo = []
            for line in content:
                # Check for date
                date_match = date_pattern.search(line)
                if date_match:
                    date = date_match.group(1)
                    if current_date != date and current_convo:
                        conversations.append(current_convo)
                        current_convo = []
                    current_date = date

                # Extracting user and message
                user_msg_match = user_msg_pattern.match(line)
                if user_msg_match:
                    user, msg = user_msg_match.groups()
                    if current_convo and current_convo[-1][0] == user:
                        current_convo[-1][5] += f",{msg.strip()}"
                    else:
                        current_convo.append([user, msg.strip()])

            # Adding the last conversation if any
            if current_convo:
                conversations.append(current_convo)

            # Formatting conversations
            adjusted_conversations = []
            for convo in conversations:
                history = []
                for i in range(0, len(convo) - 1, 2):  # Increment by 2 to ensure one question and one answer
                    prompt = convo[i][6]
                    response = convo[i + 1][7] if i + 1 < len(convo) else None
                    if response:  # Only add to the list if there's a response
                        adjusted_conversations.append({
                            "prompt": prompt,
                            "response": response,
                            "history": history.copy()
                        })
                        history.append([prompt, response])

            # Appending the results to output.txt, one object per line
            with open(output_file_path, 'a', encoding='utf-8') as output_file:
                for convo in adjusted_conversations:
                    json.dump(convo, output_file, ensure_ascii=False)
                    output_file.write('\n')

清洗后数据,可以看到已经有非常完整的逻辑关系了
cleaned_data.jpg

我在训练时使用了70%的训练集,30%作为测试集。3000条数据在我的2080显卡需要10小时!都去试试看效果吧:)

OPA是一种开源通用策略引擎,可在整个堆栈中实现统一的、上下文感知的策略实施。该项目于2018年4月被CNCF沙箱接受,2021年2月4日正式毕业于CNCF。来自大约 30 个组织的 90 多人为 OPA 做出了贡献,维护者来自包括 Google、Microsoft、VMware 和 Styra。

简单来说,是在服务上抽象一层,统一控制、审计,本文讨论仅限在Kubernetes中的gatekeeper,对容器创建进行安全约束,确保符合运维规范。

opa-gatekeeper.png

  1. 安装过程略 https://www.openpolicyagent.org/docs/latest/kubernetes-introduction/
  2. 文件结构,规则、范围一一对应。例:default命名空间必须设置探针,规则名 k8srequiredprobes.yaml ,应用范围名 default_ns_must_have_probes.yaml

- 阅读剩余部分 -

上次的数据库故障余波未平。老服务整改周期内仍有可能增高,有没什么方法限制单个pod只能建立一定数量的数据库连接,把事故控制在一定范围内

  • 首先是数据库层面,可以在配置文件中限制连接数,但基于容器的环境IP会有变化 pass
  • 其次想到的是服务网格,因为是业务标配+出色的流量控制,应该可以从这里入手。看了圈文档,Istio更多关注的是进方向
  • 再次想到kubernetes本身的网络插件也有限流的功能,calico具备对进出方向端口的限制,但没找到连接数的

陷入僵局,最笨用iptables限制,但还能实时发现pod的重启更换IP,难道要复杂化,监控结合脚本的方式吗?忽然灵光一闪,initContainers阶段不是可以做很多事情嘛

apiVersion: v1
kind: Pod
metadata:
  name: my-pod
spec:
  initContainers:
  - name: init-iptables
    image: my-iptables-image
    command: ['sh', '-c', 'iptables -A OUTPUT -p tcp --dport 3306 -m connlimit --connlimit-above 20 -j REJECT']
  containers:
  - name: my-container
    image: my-image

😅未验证,原理可行- -

某日读写分离中间件报警,有大量非业务IP连接涌入,新连接无法建立。查询数据库连接有大量的”unauthenticated user 1.2.3.4:37414 NULL Connect NULL Reading from net NULL”。一时间大量用户报障,“登录失效”、“设备断连”、“影响产线生产”等等
解决过程倒不复杂,跳过中间件恢复。售后工程师承认是中间件设计问题,释放连接逻辑bug,也提到了有大量连接时可能触发!

反观自身,架构设计阶段把过多的压力放在主库,19年上线的读写分离中间件就是“业务迭代优先,没时间基础设施改造”、“历史包袱”背景下的缓兵之计。阿里云的问题有几点
1.中间件控制台无法显示真实IP,故障后对方研发回复“日志因升级规格消失?”
2.假死后控制台无法重启
3.控制台监控不准确,使用者无法准确选择
4.1.2版本释放连接逻辑bug
主因是内部某服务突然建立了大量连接,进而引发的故障。

  • 开发阶段的考虑对运维阶段的影响:

开发阶段把更多的重点放在功能实现和业务迭代上,而忽略了基础设施的可扩展性。这可能会造成短期内的业务顺利进行,但长期看来,如果基础设施不能跟上业务的发展,最终可能会形成技术债务,导致在运维阶段遇到无法解决或者处理复杂度高的问题。

  • 对云服务商SLA的信任问题:

云服务提供商的SLA(服务等级协议)是我们选择使用其服务的一个重要依据,但是是否100%信任SLA,我们需要结合自身的业务情况和对服务提供商的了解来决定。在应急情况下,我们可能需要更具备自主的故障应对能力,而不是完全依赖服务提供商的SLA。

在对内部OS部门优化的过程中发现,服务器整体利用率很好,编译时可以将服务器所有线程打满,唯一掉链子的时候是解压缩unzip环节,只有单线程升高。简单了解了下,原来已经有了多线程的pigz工具,格式做一些微调即可。详细评测https://zhuanlan.zhihu.com/p/389817246
在翻看docker源码时,发现也会将pigz等压缩工具优先docker_source_code.jpg