LLM的测试工具:LaVague平替成国内大模型

本文涉及的产品
智能开放搜索 OpenSearch行业算法版,1GB 20LCU 1个月
实时数仓Hologres,5000CU*H 100GB 3个月
实时计算 Flink 版,5000CU*H 3个月
简介: LaVague是一款使用LLM将自然语言转换为Selenium代码以实现自动化测试的工具。由于huggingface的某些模型需付费且访问不便,本文介绍了如何使用智谱的免费大模型作为替代。智谱提供免费的embedding模型和大量令牌。

LLM的测试工具:LaVague平替成国内大模型

laVague 是将自然语言转化成浏览器交互的操作,完成自动化测试的大模型的agent。

LaVague介绍

LaVague 通过LLM将自然语言转换Selenium的代码引擎,用户或其他人工智能轻松实现自动化。
LaVague通过Llama Index实现了自然语言到python的selenium代码的编写能力,例子中提供了在线调用huggingface的LLM以及本地LLM两种方式,在线调用huggingface的Nous-Hermes-2-Mixtral-8x7B-DPO模型和BAAI/bge-small-en-v1.5的embedding模型实现了上面代码生成,但是要试用huggingface的api必须是pro付费会员,而且访问起来也不方便。
本地大模型的方式需要将模型现在到本地,并且在本地的显里面,那么开发笔记本就需要一个顶级配置的显卡,笔者也没办法解决。
想要尝鲜,怎么办?

智谱免费大模型平替huggingface的付费模型

智谱提供了embedding模型,并且免费账号提供100万个token,实名制再送400万。笔者仔细一算,还是够用的,因此就开始走上了智普大模型完成LaVague的大模型部分的替换工作。(注册步骤省略

LaVague是基于llama index完成的开发,那么并且提供了CustomLLM类方便自己扩充,因此我们基于这个构建一个调取智谱大模型的ChatGLM类

class ChatGLM(CustomLLM):
    num_output: int = DEFAULT_NUM_OUTPUTS
    context_window: int = Field(default=DEFAULT_CONTEXT_WINDOW,description="The maximum number of context tokens for the model.",gt=0,)
    model: str = Field(default=DEFAULT_MODEL, description="The ChatGlM model to use. glm-4 or glm-3-turbo")
    api_key: str = Field(default=None, description="The ChatGLM API key.")
    reuse_client: bool = Field(default=True, description=(
            "Reuse the client between requests. When doing anything with large "
            "volumes of async API calls, setting this to false can improve stability."
        ),
    )

    _client: Optional[Any] = PrivateAttr()
    def __init__(
        self,
        model: str = DEFAULT_MODEL,
        reuse_client: bool = True,
        api_key: Optional[str] = None,
        **kwargs: Any,
    )-> None:
        super().__init__(
            model=model,
            api_key=api_key,
            reuse_client=reuse_client,
            **kwargs,
        )
        self._client = None

    def _get_client(self) -> ZhipuAI:
        if not self.reuse_client :
            return ZhipuAI(api_key=self.api_key)

        if self._client is None:
            self._client = ZhipuAI(api_key=self.api_key)
        return self._client

    @classmethod
    def class_name(cls) -> str:
        return "chatglm_llm"

    @property
    def metadata(self) -> LLMMetadata:
        """Get LLM metadata."""
        return LLMMetadata(
            context_window=self.context_window,
            num_output=self.num_output,
            model_name=self.model,
        )

    def _chat(self, messages:List, stream=False) -> Any:
        response = self._get_client().chat.completions.create(
            model=self.model,  # 填写需要调用的模型名称
            messages=messages,
        )
        return response

    #@llm_chat_callback()
    def chat(self, messages: Sequence[ChatMessage], **kwargs: Any) -> ChatResponse:
        message_dicts: List = to_message_dicts(messages)
        response = self._chat(message_dicts, stream=False)
        rsp = ChatResponse(
            message=ChatMessage(content=response.choices[0].message.content, role=MessageRole(response.choices[0].message.role),
                additional_kwargs= {}),
            raw=response, additional_kwargs= get_additional_kwargs(response),
        )
        print(f"chat: {rsp} ")

        return rsp

    #@llm_chat_callback()
    def stream_chat(self, messages: Sequence[ChatMessage], **kwargs: Any) -> CompletionResponseGen:
        response_txt = ""
        message_dicts: List = to_message_dicts(messages)
        response = self._chat(message_dicts, stream=True)
        for chunk in response:

            token = chunk.choices[0].delta.content
            response_txt += token
            yield ChatResponse(message=ChatMessage(content=response_txt,role=MessageRole(message.get("role")),
                                additional_kwargs={},), delta=token, raw=chunk,)

    @llm_completion_callback()
    def complete(self, prompt: str, **kwargs: Any) -> CompletionResponse:
        messages = [{"role": "user", "content": prompt}]
        try:
            response = self._chat(messages, stream=False)

            rsp = CompletionResponse(text=str(response.choices[0].message.content), 
                                     raw=response, 
                                     additional_kwargs=get_additional_kwargs(response),)
        except Exception as e:
            print(f"complete: exception {e}")

        return rsp

    @llm_completion_callback()
    def stream_complete(self, prompt: str, **kwargs: Any) -> CompletionResponseGen:
        response_txt = ""
        messages = [{"role": "user", "content": prompt}]
        response = self._chat(messages, stream=True)
        CompletionResponse(text=response.choices[0].message.content, delta=response.choices[0].message)

        for chunk in response.choices[0].message.content.splitlines():


            try:

                token = chunk+"\r\n"

            except:
                print(f"stream exception :{chunk}")
                continue


            response_txt += token

            yield CompletionResponse(text=response_txt, delta=token)

完成LLM调用类的封装后,我们看ActionEngine类中,既需要LLM也需要emmbedding模型,但是我们不用LLM替换embedding 模型。Embedding model指的文本的表征向量,我们对embedding model的主要期望是能抓住文本的语义信息。有些模型如BERT专门做特定的训练以提升模型的语义理解能力。而LLM的主要任务是在做next token prediction,即循环输出合适的下一个单词。通常,LLM是在大规模文本数据集上做token prediction预训练,再fine-tuned以适配各种具体NLP任务,包括翻译、聊天机器人、Q&A等。这些任务的基本要求就是生成的文字是流畅的,因此,LLM主要focus on生成连贯的文本,对中间层embedding的语义约束变弱了。Decoder-only的LLM这个情况更明显。类比来说,就像我们上学时学物理一样,学物理(LLM)需要我们掌握一定的数学知识(文本的语义信息),但只学物理的话很难把数学考好。从神经网络训练的角度来说,训练LLM的training objective没有专门往文本语义信息上靠。通过继承Llama index的BaseEmbedding,实现embedding 模型的调用,代码如下:

class ChatGLMEmbeddings(BaseEmbedding):
    model: str = Field(default='embedding-2', description="The ChatGlM model to use. embedding-2")
    api_key: str = Field(default=None, description="The ChatGLM API key.")
    reuse_client: bool = Field(default=True, description=(
            "Reuse the client between requests. When doing anything with large "
            "volumes of async API calls, setting this to false can improve stability."
        ),
    )

    _client: Optional[Any] = PrivateAttr()
    def __init__(
        self,
        model: str = 'embedding-2',
        reuse_client: bool = True,
        api_key: Optional[str] = None,
        **kwargs: Any,
    )-> None:
        super().__init__(
            model=model,
            api_key=api_key,
            reuse_client=reuse_client,
            **kwargs,
        )
        self._client = None

    def _get_client(self) -> ZhipuAI:
        if not self.reuse_client :
            return ZhipuAI(api_key=self.api_key)

        if self._client is None:
            self._client = ZhipuAI(api_key=self.api_key)
        return self._client

    @classmethod
    def class_name(cls) -> str:
        return "ChatGLMEmbedding"

    def _get_query_embedding(self, query: str) -> List[float]:
        """Get query embedding."""
        return self.get_general_text_embedding(query)

    async def _aget_query_embedding(self, query: str) -> List[float]:
        """The asynchronous version of _get_query_embedding."""
        return self.get_general_text_embedding(query)

    def _get_text_embedding(self, text: str) -> List[float]:
        """Get text embedding."""
        return self.get_general_text_embedding(text)

    async def _aget_text_embedding(self, text: str) -> List[float]:
        """Asynchronously get text embedding."""
        return self.get_general_text_embedding(text)

    def _get_text_embeddings(self, texts: List[str]) -> List[List[float]]:
        """Get text embeddings."""
        embeddings_list: List[List[float]] = []
        for text in texts:
            embeddings = self.get_general_text_embedding(text)
            embeddings_list.append(embeddings)

        return embeddings_list

    async def _aget_text_embeddings(self, texts: List[str]) -> List[List[float]]:
        """Asynchronously get text embeddings."""
        return self._get_text_embeddings(texts)

    def get_general_text_embedding(self, prompt: str) -> List[float]:
        response = self._get_client().embeddings.create(
            model=self.model, #填写需要调用的模型名称
            input=prompt,
        )
        return response.data[0].embedding

chatGLM的levague

完成LLM和embedding模型的代码后,就需要模拟huggingface_lavague.py写一个chatGLM的chatglm_lavague.py,代码如下:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
'''
@File    :   chatglm_lavague.py
@Time    :   2024/03/18 16:33:29
@Author  :   CrissChan 
@Version :   1.0
@Site    :   https://blog.csdn.net/crisschan
@Desc    :   None
'''


import locale

from chatglm import ChatGLM,ChatGLMEmbeddings

from llama_index.core import Document
from llama_index.core.node_parser import CodeSplitter
from llama_index.retrievers.bm25 import BM25Retriever
from llama_index.core import VectorStoreIndex
from llama_index.core.query_engine import RetrieverQueryEngine
from llama_index.core import get_response_synthesizer
from llama_index.core import PromptTemplate

import gradio as gr
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.keys import Keys
# Monkey patch because stream_complete is not implemented in the current version of llama_index
from llama_index.core.base.llms.types import (
    CompletionResponse,
)
locale.getpreferredencoding = lambda: "UTF-8"
ZHIPU_API_KEY=<your_key>
# try:
# #   from google.colab import userdata
#   HF_TOKEN = userdata.get('HF_TOKEN')
# except:
#   import os
#   HF_TOKEN = os.environ["HF_TOKEN"]

# if not HF_TOKEN:
#   from getpass import getpass
#   HF_TOKEN = getpass('Enter your HF Token (https://huggingface.co/docs/hub/en/security-tokens): ')

model_id = "glm-4"
# max_new_tokens = 512


def stream_complete(
    self, prompt: str, formatted: bool = False, **kwargs
):
  def gen():
    text = ""
    for x in self._sync_client.text_generation(
            prompt, **{**{"max_new_tokens": self.num_output, "stream": True}, **kwargs}
        ):
      text += x
      yield CompletionResponse(text=text, delta=x)
  return gen()

## chatglm modle
llm = ChatGLM(model='glm-4', reuse_client=True, api_key=ZHIPU_API_KEY,)



embed_model = "embedding-2"
embedder = ChatGLMEmbeddings(model='embedding-2', reuse_client=True, api_key=ZHIPU_API_KEY,)



with open("prompt_template.txt", "r") as file:
  PROMPT_TEMPLATE_STR = file.read()



# Preparing the action engine


MAX_CHARS = 1500
K = 3

class ActionEngine:
    def __init__(self, llm, embedding):
        self.llm = llm
        self.embedding = embedding

    def _get_index(self, html):
        text_list = [html]
        documents = [Document(text=t) for t in text_list]

        splitter = CodeSplitter(
            language="html",
            chunk_lines=40,  # lines per chunk
            chunk_lines_overlap=200,  # lines overlap between chunks
            max_chars=MAX_CHARS,  # max chars per chunk
        )
        nodes = splitter.get_nodes_from_documents(documents)
        nodes = [node for node in nodes if node.text]

        index = VectorStoreIndex(nodes, embed_model=self.embedding)

        return index

    def get_query_engine(self, state):
        html = state
        index = self._get_index(html)

        retriever = BM25Retriever.from_defaults(
            index=index,
            similarity_top_k=K,
        )

        response_synthesizer = get_response_synthesizer(streaming=True, llm=self.llm)

        # assemble query engine
        query_engine = RetrieverQueryEngine(
            retriever=retriever,
            response_synthesizer=response_synthesizer,
        )

        prompt_template = PromptTemplate(PROMPT_TEMPLATE_STR)

        query_engine.update_prompts(
            {"response_synthesizer:text_qa_template": prompt_template}
        )

        return query_engine




# Code execution in action
MAX_CHARS = 1500

action_engine = ActionEngine(llm, embedder)

## Setup chrome options
chrome_options = Options()
chrome_options.add_argument("--headless") # Ensure GUI is off
chrome_options.add_argument("--no-sandbox")
chrome_options.add_argument("--window-size=1600,900")


title = """
<div align="center">
  <h1> Welcome to ChatGLM-LaVague crisschan</h1>
  <p>Redefining internet surfing by transforming natural language instructions into seamless browser interactions.</p>
</div>
"""

# Choose Chrome Browser
driver = webdriver.Chrome(service=webdriver_service, options=chrome_options)

# action_engine = ActionEngine(llm, embedder)

def process_url(url):
    driver.get(url)
    driver.save_screenshot("screenshot.png")
    # This function is supposed to fetch and return the image from the URL.
    # Placeholder function: replace with actual image fetching logic.
    return "screenshot.png"

def process_instruction(query):
    state = driver.page_source
    query_engine = action_engine.get_query_engine(state)
    streaming_response = query_engine.query(query)

    source_nodes = streaming_response.get_formatted_sources(MAX_CHARS)

    response = ""

    for text in streaming_response.response_gen:
    # do something with text as they arrive.
        response += text
        yield response, source_nodes

def exec_code(code):
    #
    # code = "#"+code.split("```")[1]
    code = "#"+code.split("```")[1]
    try:
        # if len(code)==0:
        #     return  "No code Generated"
        # else:
        exec(code)
        print(code)
        return "Successful code execution", code
    except Exception as e:
        output = f"Error in code execution: {str(e)}"
        return output, code

def update_image_display(img):
    driver.save_screenshot("screenshot.png")
    url = driver.current_url
    return "screenshot.png", url

def create_demo(base_url, instructions):
    with gr.Blocks() as demo:
        with gr.Row():
            gr.HTML(title)
        with gr.Row():
            url_input = gr.Textbox(value=base_url, label="Enter URL and press 'Enter' to load the page.")

        with gr.Row():
            with gr.Column(scale=8):
                image_display = gr.Image(label="Browser", interactive=False)

            with gr.Column(scale=2):
                text_area = gr.Textbox(label="Instructions")
                gr.Examples(examples=instructions, inputs=text_area,

                )
                generate_btn = gr.Button(value="Execute")
                code_display = gr.Code(label="Generated code", language="python",
                                        lines=5, interactive=False)
                with gr.Accordion(label="Logs", open=False) as log_accordion:
                    log_display = gr.Textbox(interactive=False)
                    source_display = gr.Textbox(label="Retrieved nodes", interactive=False)
        # Linking components
        url_input.submit(process_url, inputs=url_input, outputs=image_display)
        generate_btn.click(process_instruction, inputs=text_area, outputs=[code_display, source_display]).then(
            exec_code, inputs=code_display, outputs=[log_display, code_display]
        ).then(
            update_image_display, inputs=image_display, outputs=[image_display, url_input]
        )
    demo.launch(share=False)


if __name__ == "__main__":
    base_url = "https://bing.com/"

    instructions = ["click 'Search the web, input 'crisschan' and press 'Enter'",
                    "click on the Copilot on the menu ",
                "click ’有问题尽量问我‘, and input’hello’,then press ‘Enter’",
                "Scroll by 500 pixels",]

    create_demo(base_url, instructions)

需要替换成你自己的key。完成后,运行就可以看到如下gradio的页面了。

在url的后面按下回车,就会访问bing主页

然后选择已经写好的操作脚本后执行,等待一会(免费的就是速度慢,需要耐心等几分钟)。就可以在Generate Code的row里面看到生成的python selenium代码,并且完成最终页面的测试执行工作,左侧是预览图。

PS:免费薅的token有有效期,不用省着用。

总结

AIGC是当前LLM应用最为广泛的方式,LaVague是text生成了code,再经过执行生成的Code完成的动作的操作。这里面主要有两个问题,一个是所有的测试动作都是谓宾结构的描述,例如点击登录按钮,输入crisschan,按回车键等等,这些都是do something,这也就可以更好的识别动作到代码,通过这种do something的分析,撰写为生成测试代码而需要的prompt,然后通过LLM生成测试用的code。然后完成Code的执行,实现转译后到python测试脚本后代码执行工作,实现了从自然语言到动作的这样的生成,也就是text2action的AIGC。

至此,完成了LaVague的国内LLM平替。感兴趣就动手去试一试吧。

目录
相关文章
|
2月前
|
机器学习/深度学习 自然语言处理 PyTorch
LLM-Mixer: 融合多尺度时间序列分解与预训练模型,可以精准捕捉短期波动与长期趋势
近年来,大型语言模型(LLMs)在自然语言处理领域取得显著进展,研究人员开始探索将其应用于时间序列预测。Jin等人提出了LLM-Mixer框架,通过多尺度时间序列分解和预训练的LLMs,有效捕捉时间序列数据中的短期波动和长期趋势,提高了预测精度。实验结果显示,LLM-Mixer在多个基准数据集上优于现有方法,展示了其在时间序列预测任务中的巨大潜力。
80 3
LLM-Mixer: 融合多尺度时间序列分解与预训练模型,可以精准捕捉短期波动与长期趋势
|
2月前
|
机器学习/深度学习 人工智能 运维
企业内训|LLM大模型在服务器和IT网络运维中的应用-某日企IT运维部门
本课程是为某在华日资企业集团的IT运维部门专门定制开发的企业培训课程,本课程旨在深入探讨大型语言模型(LLM)在服务器及IT网络运维中的应用,结合当前技术趋势与行业需求,帮助学员掌握LLM如何为运维工作赋能。通过系统的理论讲解与实践操作,学员将了解LLM的基本知识、模型架构及其在实际运维场景中的应用,如日志分析、故障诊断、网络安全与性能优化等。
86 2
|
2月前
|
机器学习/深度学习 数据采集 人工智能
文档智能 & RAG 让AI大模型更懂业务 —— 阿里云LLM知识库解决方案评测
随着数字化转型的深入,企业对文档管理和知识提取的需求日益增长。阿里云推出的文档智能 & RAG(Retrieval-Augmented Generation)解决方案,通过高效的内容清洗、向量化处理、精准的问答召回和灵活的Prompt设计,帮助企业构建强大的LLM知识库,显著提升企业级文档管理的效率和准确性。
|
5天前
|
人工智能 自然语言处理 前端开发
CodeArena:在线 LLM 编程竞技场!用于测试不同开源 LLM 的编程能力,实时更新排行榜
CodeArena 是一个在线平台,用于测试和比较不同大型语言模型(LLM)的编程能力。通过实时显示多个 LLM 的代码生成过程和结果,帮助开发者选择适合的 LLM,并推动 LLM 技术的发展。
32 7
CodeArena:在线 LLM 编程竞技场!用于测试不同开源 LLM 的编程能力,实时更新排行榜
|
16天前
|
机器学习/深度学习 人工智能 自然语言处理
深挖大模型幻觉!哈佛大学最新报告:LLM等价于众包,只是在输出网络共识
大型语言模型(LLM)如ChatGPT正改变人机交互,但在生成看似真实的错误信息方面存在“幻觉”问题。这种现象源于LLM依赖统计概率而非语义理解,导致在处理争议或冷门话题时易出错。研究显示,LLM的准确性高度依赖于训练数据的质量和数量。尽管如此,LLM仍具巨大潜力,需持续优化并保持批判性使用。
41 12
|
19天前
|
人工智能 自然语言处理
大模型在装傻!谷歌苹果最新发现:LLM知道但不告诉你,掌握知识比表现出来的多
在AI领域,大模型(LLM)展现出了惊人的进步,但在谷歌和苹果的最新研究中,发现这些模型有时会故意“装傻”,即使已知正确答案也不告知用户。这种“隐藏智慧”现象揭示了大模型可能具备超出表面表现的深层能力,对AI评估与应用提出了新挑战,同时也带来了设计更高效模型的新机遇。论文链接:https://arxiv.org/pdf/2410.02707
33 11
|
1月前
|
自然语言处理 安全 测试技术
基于大模型的应用的测试的一些注意事项
大模型应用测试需注意三大冲突:时间敏感性冲突,即模型数据可能随时间变得过时;数据真实性冲突,指训练数据中可能存在虚假信息,影响模型准确性;数据一致性冲突,表现为模型对语义相同但句法不同的输入反应不一。测试时应针对这些问题设计用例,确保模型性能。
60 4
|
1月前
|
自然语言处理 开发者
多模态大模型LLM、MLLM性能评估方法
针对多模态大模型(LLM)和多语言大模型(MLLM)的性能评估,本文介绍了多种关键方法和标准,包括模态融合率(MIR)、多模态大语言模型综合评估基准(MME)、CheckList评估方法、多模态增益(MG)和多模态泄露(ML),以及LLaVA Bench。这些方法为评估模型的多模态和多语言能力提供了全面的框架,有助于研究者和开发者优化和改进模型。
|
1月前
|
机器学习/深度学习 人工智能 自然语言处理
大模型强崩溃!Meta新作:合成数据有剧毒,1%即成LLM杀手
在人工智能领域,大型语言模型(LLMs)的快速发展令人瞩目,但递归生成数据可能导致“模型崩溃”。Meta的研究揭示,模型在训练过程中会逐渐遗忘低概率事件,导致数据分布偏差。即使少量合成数据(如1%)也会显著影响模型性能,最终导致崩溃。研究强调保留原始数据的重要性,并提出社区合作和技术手段来区分合成数据和真实数据。论文地址:https://www.nature.com/articles/s41586-024-07566-y
73 2
|
1月前
|
人工智能 自然语言处理 算法
政务培训|LLM大模型在政府/公共卫生系统的应用
本课程是TsingtaoAI公司面向某卫生统计部门的政府职员设计的大模型技术应用课程,旨在系统讲解大语言模型(LLM)的前沿应用及其在政府业务中的实践落地。课程涵盖从LLM基础知识到智能化办公、数据处理、报告生成、智能问答系统构建等多个模块,全面解析大模型在卫生统计数据分析、报告撰写和决策支持等环节中的赋能价值。
59 2