使用 Astra DB、LangChain 和 Vercel 构建维基百科聊天机器人

一、说明

        你有多少次问谷歌一个问题,只是为了得到一个维基百科的链接,需要你点击、加载网站并滚动才能找到答案?那么自动问题搜索又是如何呢?

        维基百科是搜索引擎的顶级搜索结果,因为它是一个值得信赖的网站;人们认为那里的信息是可靠和权威的。那么为什么不直接去维基百科呢,对吧?好吧,如果你试图直接去维基百科问你的问题,你可能会得到一个“不存在”的错误以及相关页面的列表,你仍然在寻找答案。

        我们热爱维基百科以及他们为知识民主化所做的一切,并决定直面这个问题。因此,我们建立了WikiChat,这是一种使用以下工具向维基百科提问并获取自然语言答案的方法:Next.js、LangChain、Vercel、OpenAI、Cohere和DataStax Astra DB。

        WikiChat使用前1000个最受欢迎的维基百科页面进行引导,然后使用维基百科的实时更新提要来更新其信息存储。Astra DB 的一个令人敬畏的功能是它能够同时摄取这些更新,重新编制索引,并使其可供用户查询,而不会延迟重新构建索引。

        WikiChat 源代码可在 Github 上找到。请继续阅读下文,深入了解我们是如何构建它的,并观看此视频,了解我、我的同事 Alex Leventer 和 LangChain 的 Jacob Lee 最近进行的演练:

二、系统架构

        与许多检索增强生成 (RAG) 应用程序类似,我们将此应用设计为两部分:创建知识库的数据引入脚本和提供对话体验的 Web 应用。

对于数据摄取,我们构建了一个要抓取的来源列表,使用 LangChain 对文本数据进行分块,使用 Cohere 创建嵌入,然后将所有这些存储在 Astra DB 中。

对于对话式用户体验,我们正在构建 Next.js、Vercel 的 AI 库、Cohere 和 OpenAI。当用户提出问题时,我们使用 Cohere 为该问题创建嵌入,使用向量搜索查询 Astra DB,然后将这些结果输入 OpenAI 以创建对用户的对话响应。

三、设置

        若要自行构建此应用程序或启动并运行我们的存储库,需要以下几点:

  1. 免费的 Astra DB 帐户
  2. 一个免费的Cohere帐户
  3. OpenAI 帐户

        注册 Astra DB 后,您需要创建一个新的矢量数据库。继续登录您的帐户。

        接下来,创建一个新的 Serverless Vector 数据库。为其指定任何您喜欢的名称,然后选择您首选的提供商和区域。   

        在等待数据库配置时,将项目根目录下的 .env.example 文件复制到 .env。您将使用它来存储我们将用于构建此应用程序的 API 的秘密凭据和配置信息。

        创建数据库后,创建一个新的应用程序令牌。

        当模式弹出时,单击“复制”按钮并将该值粘贴到 .env 文件中的 ASTRA_DB_APPLICATION_TOKEN 中。接下来,复制您的 API 端点:

        将该值粘贴到 .env 文件中的 ASTRA_DB_EN​​DPOINT 中。

        登录您的 Cohere 帐户并转到 API 密钥。您将找到可用于开发的试用 API 密钥;继续复制该值并将其存储在 COHERE_API_KEY 键下。

        最后,登录您的 OpenAI 帐户并创建一个新的 API 密钥。将该 API 密钥存储在您的 .env 中作为 OPENAI_API_KEY。

        好的,就是这样 - 是时候编写一些代码了!

四、引入数据

        在目录中,您将找到处理WikiChat所有数据摄取的Python脚本。它加载第一批流行的维基百科文章,然后使用维基百科发布的事件流监听对英语文章的更改。scripts

        注意:事件流包括多种形式的更新,例如机器人、对讨论页的更改,甚至是为测试系统而发送的金丝雀事件。无论来源如何,每篇文章都要经过五个步骤才能被摄取。

        在执行摄取步骤之前,让我们快速浏览一下将使用 Data API 存储在 Astra 中的数据。数据 API 使用 JSON 文档存储数据,这些文档在集合中分组在一起。默认情况下,文档中的每个字段都已编制索引并可供查询,包括嵌入向量。对于WikiChat应用程序,我们创建了三个集合:

  • article_embeddings- 每个文档存储来自文章的一块文本和一个使用 Cohere 创建的嵌入向量。这是WikiChat回答您的聊天问题所需的核心信息。
  • article_metadata- 每个文档都存储了有关我们摄取的单篇文章的元数据,包括我们上次摄取时所包含的块的相关信息。
  • article_suggestions- 此集合包含一个文档,脚本会不断更新该文档以跟踪最近处理的 5 篇文章,以及每个文档的前 5 个块或最近更新的 5 个块。

        scripts/wikichat/database.py 文件负责初始化 astrapy 客户端库,调用数据 API 来创建集合,并创建客户端对象来使用它们。我们唯一需要做的数据建模是我们想要存储在每个集合中的 Python 类。这些在scripts/wikichat/processing/model.py 文件中定义。文件的前半部分定义了我们用来通过下面讨论的管道传递文章的类,而后半部分定义了我们想要存储在 Astra 中的类。这些类都被定义为标准的Python数据类; Astra 中存储的类还使用 dataclasses-json,因为该库可以将数据类层次结构与存储在 Astra 中的 Python 字典进行序列化。        

例如,ChunkedArticleMetadataOnly 类存储在article_metadata 集合中,定义为:

@dataclass_json
@dataclass
class ChunkedArticleMetadataOnly:
    _id: str
    article_metadata: ArticleMetadata
    chunks_metadata: dict[str, ChunkMetadata] = field(default_factory=dict)
    suggested_question_chunks: list[Chunk] = field(default_factory=list)

当我们想要存储此类的对象时(在scripts/wikichat/processing/articles.py/update_article_metadata()中),我们使用类上的dataclass_json装饰添加的to_dict()方法,该方法创建一个基本的Python字典astrapy 存储为 JSON 文档:

    

METADATA_COLLECTION.find_one_and_replace(
            filter={"_id": metadata._id},
            replacement=metadata.to_dict(),
            options={"upsert": True}
        )

        当我们读回它时(在同一文件中的 calc_chunk_diff() 中),from_dict() 用于从存储的 JSON 文档重建整个对象层次结构:

resp = METADATA_COLLECTION.find_one(filter={"_id": new_metadata._id})
prev_metadata_doc = resp["data"]["document"]
prev_metadata = ChunkedArticleMetadataOnly.from_dict(prev_metadata_doc)

        随着大纲和数据访问的解决,是时候看看我们如何处理每篇文章了。文章通过使用 Python 异步 I/O 构建的处理管道传递。异步处理用于处理来自维基百科的更新的突发性,并确保我们在等待脚本需要进行的各种远程调用时继续处理。处理管道有五个步骤:

  1. load_article()从 wikipedia.org 检索文章,并使用 Beautiful Soup 从 HTML 中提取文本。
  2. chunk_article()将文章分解为多个块,这些块用于创建描述其语义含义的嵌入向量。使用 LangChain 的 RecursiveCharacterTextSplitter 对文本进行分块,并将该块的 sha256 哈希计算为消息摘要,以便可以比较块是否相等。
  3. calc_chunk_diff()首先检查 Astra 以查看我们是否有关于本文的先前元数据,然后创建一个“差异”来描述当前文章。将所有当前块的哈希值与我们上次看到本文时所知道的哈希值进行比较。以前未见过的文章将仅包含新的文本块,而以前看到的文章将包含新的、已删除的和未更改的文本块的组合。
  4. vectorize_diff()调用 Cohere 来计算文章中新文本块的嵌入。在计算“Diff”后调用意味着我们避免计算未更改的文本块的向量。
  5. store_article_diff()更新 Astra 以存储我们现在对本文的了解;这有三个步骤:
  6. update_article_metadata()更新集合中文章的元数据,并更新集合以跟踪最近的更新,以便 UI 可以提出新问题。article_metadataarticle_suggestions
  7. insert_vectored_chunks() 将所有新块及其向量插入到 article_embeddings 集合中。
  8. delete_vectored_chunks()删除更新后的文章中不再存在的所有块。

五、构建聊天机器人用户体验

        现在我们已经从维基百科预加载了一些流行的数据,并连接了内容的实时更新,是时候构建聊天机器人了!对于此应用程序,我们选择使用 Next.js,一个全栈React.js Web 框架。此 Web 应用程序的两个最重要的组件是基于 Web 的聊天界面和检索用户问题答案的服务。

        聊天界面由 Vercel 的 AI npm 库提供支持。该模块可帮助开发人员仅用几行代码构建类似 ChatGPT 的体验。在我们的应用程序中,我们在“app/page.tsx”文件中实现了此体验,该文件表示 Web 应用程序的根目录。以下是一些值得一提的代码片段:

"use client";
import { useChat, useCompletion } from 'ai/react';
import { Message } from 'ai';

        “use client”;指令告诉 Next.js 该模块只会在客户端上运行。 import 语句使 Vercel 的 AI 库在我们的应用程序中可用。

   

const { append, messages, isLoading, input, handleInputChange, handleSubmit } = useChat();

        这会初始化 useChat React 钩子,该钩子处理用户与聊天机器人交互时的状态和大部分交互体验。

const handleSend = (e) => {
  handleSubmit(e, { options: { body: { useRag, llm, similarityMetric}}});
}

        当用户提出问题时,此函数负责将该信息传递给后端服务,以找出答案是什么。

const [suggestions, setSuggestions] = useState<PromptSuggestion[]>([]);
  
const { complete } = useCompletion({
    onFinish: (prompt, completion) => {
      const parsed = JSON.parse(completion);
      const argsObj = JSON.parse(parsed?.function_call.arguments);
      const questions = argsObj.questions;
      
      const questionsArr: PromptSuggestion[] = [];
      questions.forEach(q => {
        questionsArr.push(q);
      });
      setSuggestions(questionsArr);
    }
  });
  useEffect(() => {
    complete('')
  }, []);

        这将初始化另一个重要的钩子,我们用它来根据我们索引的维基百科中最近更新的页面加载建议的问题。处理程序从服务器接收 JSON 有效负载,用于在 UI 中显示。让我们在服务器端深入研究这个问题,看看这些建议的问题是如何创建的。onFinishsetSuggestions

六、预先填充一些建议的问题以开始

        如前所述,当用户首次加载 WikiChat 时,它会提供一些建议问题,这些问题基于最近更新并由应用程序摄取的维基百科页面。但是我们如何从最近更新的页面转到建议的问题呢?让我们检查 /api/completion/route.ts 看看发生了什么:

import { AstraDB } from "@datastax/astra-db-ts";
import { OpenAIStream, StreamingTextResponse } from "ai";
import OpenAI from "openai";
import type { ChatCompletionCreateParams } from 'openai/resources/chat';

        在这里,我们将导入以下资源:Astra DB 客户端、Vercel AI SDK 中的一些帮助程序、OpenAI 客户端以及我们稍后将讨论的帮助程序类型。

const {
  ASTRA_DB_APPLICATION_TOKEN,
  ASTRA_DB_ENDPOINT,
  ASTRA_DB_SUGGESTIONS_COLLECTION,
  OPENAI_API_KEY,
} = process.env;

const astraDb = new AstraDB(ASTRA_DB_APPLICATION_TOKEN, ASTRA_DB_ENDPOINT);

const openai = new OpenAI({
  apiKey: OPENAI_API_KEY,
});

接下来,我们根据 .env 文件中配置的密钥初始化 Astra DB 和 OpenAI 客户端。


const suggestionsCollection = await astraDb.collection(ASTRA_DB_SUGGESTIONS_COLLECTION);

const suggestionsDoc = await suggestionsCollection.findOne(
  {
    _id: "recent_articles"
  },
  {
    projection: {
      "recent_articles.metadata.title" : 1,
      "recent_articles.suggested_chunks.content" : 1,
  },
});

        还记得当我们讨论摄取过程时,我们将最近更新的五篇维基百科文章存储在数据库的文档中吗?在这里,我们使用客户端的 findOne 函数查询该文档。该选项使我们能够告诉客户端仅返回我们指定的文档的属性。projection

const docMap = suggestionsDoc.recent_articles.map(article => {
        return {
          pageTitle: article.metadata.title,
          content: article.suggested_chunks.map(chunk => chunk.content)
        }
      });

      docContext = JSON.stringify(docMap);

一旦我们有了文档,我们就用它来创建一个简单的“页面标题”和“内容”对的数组对象,当我们调用 LLM 时,我们将将其作为上下文传递。

const response = await openai.chat.completions.create({
  model: "gpt-3.5-turbo-16k",
  stream: true,
  temperature: 1.5,
  messages: [{
    role: "user",
    content: `You are an assistant who creates sample questions to ask a chatbot. 
      Given the context below of the most recently added data to the most popular pages 
      on Wikipedia come up with 4 suggested questions. Only write no more than one 
      question per page and keep them to less than 12 words each. Do not label which page 
      the question is for/from.
      START CONTEXT
      ${docContext}
      END CONTEXT
    `,
    }],
  functions
});

        现在我们已经有了最近更新的维基百科页面的数据(标题和内容),您可能想知道我们如何将其转化为我们应用程序的建议问题。好吧,如果有疑问,请法学硕士弄清楚!

        在对 OpenAI 聊天完成 API 的调用中,我们构建了一个提示,要求 LLM 使用传递的数据来构建适当的问题。我们提供有关问题类型、问题应该持续多长时间的说明,并将温度设置为 1.5(值范围为 0-2),以获得更有创意的回答。

          函数的最后一个参数允许我们传入自定义函数。在我们的例子中,我们使用它来定义从 OpenAI 返回的响应的“形状”,以便我们可以轻松解析它并使用它来填充 UI 中的建议问题。

const functions: ChatCompletionCreateParams.Function[] = [{
  name: 'get_suggestion_and_category',
  description: 'Prints a suggested question and the category it belongs to.',
  parameters: {
    type: 'object',
    properties: {
      questions: {
        type: 'array',
        description: 'The suggested questions and their categories.',
        items: {
          type: 'object',
          properties: {
            category: {
              type: 'string',
              enum: ['history', 'science', 'sports', 'technology', 'arts', 'culture',
                'geography', 'entertainment', 'politics', 'business', 'health'],
              description: 'The category of the suggested question.',
            },
            question: {
              type: 'string',
              description: 'The suggested question.',
            },
          },
        },
      },
    },
    required: ['questions'],
  },
}];

        在这个有效负载的深处,有两个我们正在定义并期望返回的关键值。第一个是类别,它是一个字符串,是我们用来在应用程序 UI 中设置图标的少数预定义值之一。第二个是问题,一个字符串,表示要在 UI 中向用户显示的建议问题。

七、使用 RAG 回答您的问题

        现在我们已经解释了如何构建建议问题,让我们看看当用户向 WikiChat 提问时会发生什么。该请求由 /app/api/chat/route.ts 中定义的后端 API 路由处理,并广泛使用 LangChain 的 JS SDK。让我们分解一下,看看发生了什么:

import { CohereEmbeddings } from "@langchain/cohere";
import { Document } from "@langchain/core/documents";
import { 
  RunnableBranch,
  RunnableLambda,
  RunnableMap, 
  RunnableSequence
} from "@langchain/core/runnables";
import { StringOutputParser } from "@langchain/core/output_parsers";
import { PromptTemplate } from "langchain/prompts";
import {
  AstraDBVectorStore,
  AstraLibArgs,
} from "@langchain/community/vectorstores/astradb";
import { ChatOpenAI } from "langchain/chat_models/openai";
import { StreamingTextResponse, Message } from "ai";

        这些导入使 Langchain JS SDK 的相关部分可供我们使用。您会注意到,我们正在使用 Langchain 对 Cohere 和 OpenAI 的内置支持作为 LLM,并使用 Astra DB 作为向量存储。

const questionTemplate = `You are an AI assistant answering questions about anything 
  from Wikipedia the context will provide you with the most relevant data from wikipedia 
  including the pages title, url, and page content.
  If referencing the text/context refer to it as Wikipedia.
  At the end of the response add one markdown link using the format: [Title](URL) and 
  replace the title and url with the associated title and url of the more relavant page  
  from the context
  This link will not be shown to the user so do not mention it.
  The max links you can include is 1, do not provide any other references or annotations.
  if the context is empty, answer it to the best of your ability. If you cannot find the    
  answer user's question in the context, reply with "I'm sorry, I'm only allowed to 
  answer questions related to the top 1,000 Wikipedia pages".
<context>
  {context}
</context>
 
QUESTION: {question}  
`;
const prompt = PromptTemplate.fromTemplate(questionTemplate);

        问题模板是我们用来为 LLM 构建提示的模板,我们可以在其中注入额外的上下文,以便它提供最佳答案。这些说明是相当不言自明的,请注意,我们指示它以 Markdown 格式提供指向维基百科上源页面的链接。稍后,当我们在 UI 中呈现答案时,我们将利用这一点。

const {messages, llm } = await req.json();
const previousMessages = messages.slice(0, -1);
const latestMessage = messages[messages?.length - 1]?.content;

const embeddings = new CohereEmbeddings({
  apiKey: COHERE_API_KEY,
  inputType: "search_query",
  model: "embed-english-v3.0",
});
const chatModel = new ChatOpenAI({
  temperature: 0.5,
  openAIApiKey: OPENAI_API_KEY,
  modelName: llm ?? "gpt-4",
  streaming: true,
});

        在 POST 函数内部,我们接收聊天历史记录(消息)的值,并使用这些值来定义 previousMessages 和latestMessage。接下来,我们初始化 Cohere 和 OpenAI 以在 LangChain 中使用。

const astraConfig: AstraLibArgs = {
  token: ASTRA_DB_APPLICATION_TOKEN,
  endpoint: ASTRA_DB_ENDPOINT,
  collection: “article_embeddings”,
  contentKey: “content”
};

const vectorStore = new AstraDBVectorStore(embeddings, astraConfig);

await vectorStore.initialize();
 
const retriever = vectorStore.asRetriever(10);

        现在是时候为 LangChain 配置 Astra 数据库向量存储了,我们在其中指定了连接凭据,要从中查询的集合以及从数据库返回的 10 个文档的限制。

const chain = RunnableSequence.from([
  condenseChatBranch,
  mapQuestionAndContext,
  prompt,
  chatModel,
  new StringOutputParser(),
]).withConfig({ runName: "chatChain"});

const stream = await chain.stream({
  chat_history: formatVercelMessages(previousMessages),
  question: latestMessage, 
});

        这就是LangChain魔术发生✨的地方

        首先,我们通过传入一系列 Runnables 来创建一个 RunnableSequence。此时,您需要知道的是,RunnableSequence 从顶部开始,执行每个 Runnable,并将其输出作为输入传递给下一个 Runnable。

        定义序列后,我们使用聊天记录和最近的问题执行它。这个序列中发生了很多事情,所以让我们检查一下每件作品。

const hasChatHistoryCheck = RunnableLambda.from(
  (input: ChainInut) => input.chat_history.length > 0
);

const chatHistoryQuestionChain = RunnableSequence.from([
  {
    question: (input: ChainInut) => input.question,
    chat_history: (input: ChainInut) => input.chat_history,
  },
  condenseQuestionPrompt,
  chatModel,
  new StringOutputParser(),
]).withConfig({ runName: "chatHistoryQuestionChain"});
const noChatHistoryQuestionChain = RunnableLambda.from(
  (input: ChainInut) => input.question
).withConfig({ runName: "noChatHistoryQuestionChain"});

const condenseChatBranch = RunnableBranch.from([
  [hasChatHistoryCheck, chatHistoryQuestionChain],
   noChatHistoryQuestionChain,
 ]).withConfig({ runName: "condenseChatBranch"});

        序列中的第一个 Runnable 是 。此代码的目的是使WikiChat变得智能并了解先前提出的问题。让我们举一个说明性的例子:condenseChatBranch

  • 问题#1:谁是《星球大战》中的反派?
  • 答案:达斯·维达
  • 问题#2:他的孩子是谁?

        如果不知道第一个问题是什么,第二个问题就没有意义。因此,我们定义了一个 RunnableBranch,其功能有点像 if/else 语句。如果 Runnable hasChatHistory 为 true,则 Langchain 将运行 chatHistoryQuestionChain,否则将运行 noChatHistoryChain。

        hasChatHistoryCheck 只是检查我们在初始化链时定义的 chat_history 输入,以查看是否有非空值。

        如果此检查为真,chatHistoryQuestionChain Runnable 会将问题和聊天历史记录提供给 LLM 以构建更好的问题。让我们看看 condenseQuestionPrompt 来看看它是如何工作的:

const condenseQuestionTemplate = `Given the following chat history and a follow up
 question, If the follow up question references previous parts of the chat rephrase the
 follow up question to be a standalone question if not use the follow up question as the
 standalone question.

<chat_history>
  {chat_history}
</chat_history>

Follow Up Question: {question}
Standalone question:`;

const condenseQuestionPrompt = PromptTemplate.fromTemplate(
  condenseQuestionTemplate,
);

        在这里,我们定义了一个提示,该提示考虑了我们的聊天记录,并专门指示 LLM 查看所提出的问题是否是后续问题。如果我们使用前面的例子,LLM将接受“谁是他的孩子?”的问题,并查看聊天记录,将问题改写为“谁是达斯维达的孩子?Boom — 一个更智能的聊天机器人!

        现在,如果没有聊天历史记录,则 noChatHistoryQuestionChain 将充当无操作,并且仅返回用户提出的问题(未更改)。

const combineDocumentsFn = (docs: Document[]) => {
  const serializedDocs = docs.map((doc) => `Title: ${doc.metadata.title}
URL: ${doc.metadata.url}
Content: ${doc.pageContent}`);

  return serializedDocs.join("\n\n");
};

const retrieverChain = retriever.pipe(combineDocumentsFn).withConfig({ runName:
 "retrieverChain"});

const mapQuestionAndContext = RunnableMap.from({
  question: (input: string) => input,
  context: retrieverChain
}).withConfig({ runName: "mapQuestionAndContext"});

        主序列中的下一个是 ,它传递了上一步(用户的问题)的输出,并从 Astra DB 中检索最接近的匹配文档并将它们组合成一个字符串。mapQuestionAndContext

        然后,此字符串将传递到下一步,即我们之前定义的提示。然后,我们将这个完全膨胀的提示传递给 LLM,最后将 LLM 的输出传递给 LangChain StringParser。

return new StreamingTextResponse(stream);

        最后要做的是将 Langchain 流作为 StreamingTextResponse 返回,以便用户在 LLM 通过网络时实时看到 LLM 的输出。

八、结束语

       让我们回顾一下我们构建一个智能聊天机器人所涵盖的所有内容,该机器人可以回答有关维基百科上最受欢迎和最近更新的页面的问题:

  • 通过抓取 1,000 篇最流行的维基百科文章来加载初始数据集。
  • 监听实时更新并仅处理差异。
  • 使用 LangChain 对文本数据进行智能分块,并使用 Cohere 生成嵌入。
  • 在 Astra DB 中存储应用程序和矢量数据。
  • 使用 Vercel 的 AI 库构建基于 Web 的聊天机器人 UX。
  • 在 Astra DB 上执行矢量搜索。
  • 使用 OpenAI 生成准确且上下文感知的响应。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/379115.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

《游戏引擎架构》 -- 学习2

声明&#xff0c;定义&#xff0c;以及链接规范 翻译单元 声明与定义 链接规范 C/C 内存布局 可执行映像 程序堆栈 动态分配的堆 对象的内存布局 kilobyte 和 kibibyte 流水线缓存以及优化 未完待续。。。

async 与 await(JavaScript)

目录捏 前言一、async二、await三、使用方法总结 前言 async / await 是 ES2017(ES8) 提出的基于 Promise 解决异步的最终方案。上一篇文章介绍了 回调地狱 与 Promise&#xff08;JavaScript&#xff09;&#xff0c;因为 Promise 的编程模型依然充斥着大量的 then 方法&#…

软件测试工程师——缺陷(一篇足以)

目录 定义 缺陷的类型 缺陷的严重程度 缺陷的状态 缺陷的根源 ​缺陷的来源 缺陷的起源 缺陷的生命周期 缺陷的识别 缺陷报告模板 编写缺陷报告的目的 缺陷报告编写的准则 缺陷描述的准则 定义 1. 软件未实现产品说明书中所提及的功能 2. 软件实现了产品说明书中…

stable_diffusion提示词编写笔记(1)

stable_diffusion提示词编写笔记(1) start 总结一下AI绘画学到的知识。 一.提示词分两种&#xff1a; 1.正向提示词&#xff1b; 2.反向提示词&#xff1b; 一个对应你希望图形包含的内容提示词&#xff0c;一个对应你不希望图形出现的内容提示词。 二.如何书写提示词 1.内…

连杆的形状优化

前言 本示例使用优化模块在不改变连杆体积的情况下将连杆中的应力集中降至最低。 本页讨论 前言应用描述Abaqus建模方法和仿真技术文件参考 应用描述 此示例说明了连杆的形状优化。形状优化对曲面节点在设计区域中的位置进行轻微修改&#xff0c;以实现优化的解决方案。形状优…

pwn学习笔记(2)ret_2_text_or_shellcode

pwn学习笔记&#xff08;2&#xff09; 1.三种常见的寄存器&#xff1a; ​ ax寄存器&#xff1a;通用寄存器&#xff0c;可用于存放多种数据 ​ bp寄存器&#xff1a;存放的是栈帧的栈底地址 ​ sp寄存器&#xff1a;存放的是栈顶的地址 2.栈帧与栈工作的简介&#xff1a…

Linux(Ubuntu)环境下安装卸载Python3(避免踩坑)

一、安装 第一步&#xff1a; 进入/usr/local/目录&#xff0c;下载Python3&#xff0c;这里我下载的是python 3.8.10&#xff0c;如果要下载其他版本改下链接中的版本号&#xff0c;需与官网版本号对应。 wget https://www.python.org/ftp/python/3.8.10/Python-3.8.10.tgz第…

HTML小白入门学习-表格标签

一、前言 话说上文&#xff0c;我们对HTML的表单类标签进行简单的学习和认识&#xff0c; 分别是<form>、<input>、<textarea>、<label>、<select>和<button>这几个标签。 与表单标签有一字之别的表格标签&#xff0c;就是本文的主角。本…

【MySQL】学习和总结DCL的权限控制

&#x1f308;个人主页: Aileen_0v0 &#x1f525;热门专栏: 华为鸿蒙系统学习|计算机网络|数据结构与算法 ​&#x1f4ab;个人格言:“没有罗马,那就自己创造罗马~” #mermaid-svg-Bl9kYeLf8GfpdQgL {font-family:"trebuchet ms",verdana,arial,sans-serif;font-siz…

鸿蒙(HarmonyOS)项目方舟框架(ArkUI)之Rating组件

鸿蒙&#xff08;HarmonyOS&#xff09;项目方舟框架&#xff08;ArkUI&#xff09;之Rating组件 一、操作环境 操作系统: Windows 10 专业版、IDE:DevEco Studio 3.1、SDK:HarmonyOS 3.1 二、Rating组件 提供在给定范围内选择评分的组件。 子组件 无。 接口 Rating(opt…

整合RabbitMQ实现消息异步发送

消息队列中间件 消息队列中间件是分布式系统中重要的组件&#xff0c;主要解决应用耦合&#xff0c;异步消息&#xff0c;流量削峰等问题。 中间件最标准的用法是生产者生产消息传送到队列&#xff0c;消费者从队列中拿取消息并处理&#xff0c;生产者不用关心是谁来消费&#…

一个冷门的js加密逆向分析(二)

前天发了一片js加密分析的文章&#xff0c;今天继续来说第二层加密是什么样的。 上源代码 window["" "f" "3" "2" "0" "6" "b" "1" ""] function () {;(function (v509…

【html学习笔记】1.概念

1.概念 1.1 HTML标准格式 <html><body><p>Hello World</p></body> </html>1.2 编辑方式 新建一个笔记本文件&#xff0c;将html语法格式的内容写入。保存后将记事本的.txt后缀换成.html,就可以在浏览器里运行了 1.3 中文问题 为了避…

华为数通方向HCIP-DataCom H12-821题库(单选题:461-480)

第461题 以下关于路由策略特点的描述,错误的是哪一项? A、能够修改路由属性,但是不能改变网络流量经过的路径 B、能通过控制路由器的路由表规模,来节约系统资源 C、能通过控制路由的接收、发布和引入,以提高网络的安全性 D、能通过修改路由属性,对网络数据流量可以合理规…

SQL注入(SQL Injection)从注入到拖库 —— 简单的手工注入实战指南精讲

基本SQL注入步骤&#xff1a; 识别目标&#xff1a;确定目标网站或应用程序存在潜在的SQL注入漏洞。收集信息&#xff1a;通过查看页面源代码、URL参数和可能的错误信息等&#xff0c;搜集与注入有关的信息。判断注入点&#xff1a;确定可以注入的位置&#xff0c;比如输入框、…

跟着pink老师前端入门教程-day21+22

5.4 常见flex布局思路 5.5 背景线性渐变 语法&#xff1a; background: linear-gradient( 起始方向 , 颜色 1, 颜色 2, ...); background: -webkit-linear-gradient(left, red , blue); background: -webkit-linear-gradient(left top, red , blue); 背景渐变必须添加浏览…

【若依】若依框架在本地运行的操作方法,及踩坑记录

若依框架简介 若依是一个Gitee上一个开源的基于SpringBoot开发的轻量级Java快速开发框架&#xff0c;用以快速构建后台管理系统&#xff0c;点击跳转到官方地址 本机部署过程 Step1. 下载项目源码 我选择的是直接下载zip压缩包&#xff0c;解压后得到如下文件夹&#xff0c…

挑战杯 python opencv 深度学习 指纹识别算法实现

1 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; python opencv 深度学习 指纹识别算法实现 &#x1f947;学长这里给一个题目综合评分(每项满分5分) 难度系数&#xff1a;3分工作量&#xff1a;4分创新点&#xff1a;4分 该项目较为新颖…

Java面试题(一)——MySQL索引、JVM内存模型、JDBC

1、mysql使用innodb引擎&#xff0c;请简述mysql索引的最左前缀如何优化 order by 语句&#xff1f; 1.1、核心回答 首先要对sql进行分析检查必要的查询字段&#xff0c;过滤字段&#xff0c;排序字段是否按顺序创建好了索引 如果查询字段不在索引中可能会产生回表操作会导致…

Linux环境下配置HTTP代理服务器教程

大家好&#xff0c;我是你们可爱的Linux小助手&#xff01;今天&#xff0c;我将带你们一起探索如何在Linux环境下配置一个HTTP代理服务器。请注意&#xff0c;这不是一次火箭科学的实验&#xff0c;而是一次简单而有趣的冒险。 首先&#xff0c;我们需要明确什么是HTTP代理服…