<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title>Ethan's Blog</title><link>https://blog.ethan-ai.cn/</link><description>This is my cool site</description><generator>Hugo -- gohugo.io</generator><language>zh-CN</language><lastBuildDate>Wed, 18 Jun 2025 00:00:00 +0800</lastBuildDate><atom:link href="https://blog.ethan-ai.cn/index.xml" rel="self" type="application/rss+xml"/><item><title>RAG系列-基础RAG（Simple RAG）</title><link>https://blog.ethan-ai.cn/%E5%9F%BA%E7%A1%80rag/</link><pubDate>Wed, 18 Jun 2025 00:00:00 +0800</pubDate><author>作者</author><guid>https://blog.ethan-ai.cn/%E5%9F%BA%E7%A1%80rag/</guid><description><![CDATA[<h1 id="01-基础ragsimple-rag">01. 基础RAG（Simple RAG）</h1>
<h2 id="方法简介">方法简介</h2>
<p>基础RAG（Retrieval-Augmented Generation）是最简单的检索增强生成方法。它通过向量化检索获取与用户查询最相关的文档片段，并将这些片段作为上下文输入给大语言模型进行答案生成。</p>
<h2 id="核心代码">核心代码</h2>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">  1
</span><span class="lnt">  2
</span><span class="lnt">  3
</span><span class="lnt">  4
</span><span class="lnt">  5
</span><span class="lnt">  6
</span><span class="lnt">  7
</span><span class="lnt">  8
</span><span class="lnt">  9
</span><span class="lnt"> 10
</span><span class="lnt"> 11
</span><span class="lnt"> 12
</span><span class="lnt"> 13
</span><span class="lnt"> 14
</span><span class="lnt"> 15
</span><span class="lnt"> 16
</span><span class="lnt"> 17
</span><span class="lnt"> 18
</span><span class="lnt"> 19
</span><span class="lnt"> 20
</span><span class="lnt"> 21
</span><span class="lnt"> 22
</span><span class="lnt"> 23
</span><span class="lnt"> 24
</span><span class="lnt"> 25
</span><span class="lnt"> 26
</span><span class="lnt"> 27
</span><span class="lnt"> 28
</span><span class="lnt"> 29
</span><span class="lnt"> 30
</span><span class="lnt"> 31
</span><span class="lnt"> 32
</span><span class="lnt"> 33
</span><span class="lnt"> 34
</span><span class="lnt"> 35
</span><span class="lnt"> 36
</span><span class="lnt"> 37
</span><span class="lnt"> 38
</span><span class="lnt"> 39
</span><span class="lnt"> 40
</span><span class="lnt"> 41
</span><span class="lnt"> 42
</span><span class="lnt"> 43
</span><span class="lnt"> 44
</span><span class="lnt"> 45
</span><span class="lnt"> 46
</span><span class="lnt"> 47
</span><span class="lnt"> 48
</span><span class="lnt"> 49
</span><span class="lnt"> 50
</span><span class="lnt"> 51
</span><span class="lnt"> 52
</span><span class="lnt"> 53
</span><span class="lnt"> 54
</span><span class="lnt"> 55
</span><span class="lnt"> 56
</span><span class="lnt"> 57
</span><span class="lnt"> 58
</span><span class="lnt"> 59
</span><span class="lnt"> 60
</span><span class="lnt"> 61
</span><span class="lnt"> 62
</span><span class="lnt"> 63
</span><span class="lnt"> 64
</span><span class="lnt"> 65
</span><span class="lnt"> 66
</span><span class="lnt"> 67
</span><span class="lnt"> 68
</span><span class="lnt"> 69
</span><span class="lnt"> 70
</span><span class="lnt"> 71
</span><span class="lnt"> 72
</span><span class="lnt"> 73
</span><span class="lnt"> 74
</span><span class="lnt"> 75
</span><span class="lnt"> 76
</span><span class="lnt"> 77
</span><span class="lnt"> 78
</span><span class="lnt"> 79
</span><span class="lnt"> 80
</span><span class="lnt"> 81
</span><span class="lnt"> 82
</span><span class="lnt"> 83
</span><span class="lnt"> 84
</span><span class="lnt"> 85
</span><span class="lnt"> 86
</span><span class="lnt"> 87
</span><span class="lnt"> 88
</span><span class="lnt"> 89
</span><span class="lnt"> 90
</span><span class="lnt"> 91
</span><span class="lnt"> 92
</span><span class="lnt"> 93
</span><span class="lnt"> 94
</span><span class="lnt"> 95
</span><span class="lnt"> 96
</span><span class="lnt"> 97
</span><span class="lnt"> 98
</span><span class="lnt"> 99
</span><span class="lnt">100
</span><span class="lnt">101
</span><span class="lnt">102
</span><span class="lnt">103
</span><span class="lnt">104
</span><span class="lnt">105
</span><span class="lnt">106
</span><span class="lnt">107
</span><span class="lnt">108
</span><span class="lnt">109
</span><span class="lnt">110
</span><span class="lnt">111
</span><span class="lnt">112
</span><span class="lnt">113
</span><span class="lnt">114
</span><span class="lnt">115
</span><span class="lnt">116
</span><span class="lnt">117
</span><span class="lnt">118
</span><span class="lnt">119
</span><span class="lnt">120
</span><span class="lnt">121
</span><span class="lnt">122
</span><span class="lnt">123
</span><span class="lnt">124
</span><span class="lnt">125
</span><span class="lnt">126
</span><span class="lnt">127
</span><span class="lnt">128
</span><span class="lnt">129
</span><span class="lnt">130
</span><span class="lnt">131
</span><span class="lnt">132
</span><span class="lnt">133
</span><span class="lnt">134
</span><span class="lnt">135
</span><span class="lnt">136
</span><span class="lnt">137
</span><span class="lnt">138
</span><span class="lnt">139
</span><span class="lnt">140
</span><span class="lnt">141
</span><span class="lnt">142
</span><span class="lnt">143
</span><span class="lnt">144
</span><span class="lnt">145
</span><span class="lnt">146
</span><span class="lnt">147
</span><span class="lnt">148
</span><span class="lnt">149
</span><span class="lnt">150
</span><span class="lnt">151
</span><span class="lnt">152
</span><span class="lnt">153
</span><span class="lnt">154
</span><span class="lnt">155
</span><span class="lnt">156
</span><span class="lnt">157
</span><span class="lnt">158
</span><span class="lnt">159
</span><span class="lnt">160
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">fitz</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">os</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">numpy</span> <span class="k">as</span> <span class="nn">np</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">json</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">openai</span> <span class="kn">import</span> <span class="n">OpenAI</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">extract_text_from_pdf</span><span class="p">(</span><span class="n">pdf_path</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;
</span></span></span><span class="line"><span class="cl"><span class="s2">    Extracts text from a PDF file and prints the first `num_chars` characters.
</span></span></span><span class="line"><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="cl"><span class="s2">    Args:
</span></span></span><span class="line"><span class="cl"><span class="s2">    pdf_path (str): Path to the PDF file.
</span></span></span><span class="line"><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="cl"><span class="s2">    Returns:
</span></span></span><span class="line"><span class="cl"><span class="s2">    str: Extracted text from the PDF.
</span></span></span><span class="line"><span class="cl"><span class="s2">    &#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># Open the PDF file</span>
</span></span><span class="line"><span class="cl">    <span class="n">mypdf</span> <span class="o">=</span> <span class="n">fitz</span><span class="o">.</span><span class="n">open</span><span class="p">(</span><span class="n">pdf_path</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">all_text</span> <span class="o">=</span> <span class="s2">&#34;&#34;</span>  <span class="c1"># Initialize an empty string to store the extracted text</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># Iterate through each page in the PDF</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="n">page_num</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">mypdf</span><span class="o">.</span><span class="n">page_count</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="n">page</span> <span class="o">=</span> <span class="n">mypdf</span><span class="p">[</span><span class="n">page_num</span><span class="p">]</span>  <span class="c1"># Get the page</span>
</span></span><span class="line"><span class="cl">        <span class="n">text</span> <span class="o">=</span> <span class="n">page</span><span class="o">.</span><span class="n">get_text</span><span class="p">(</span><span class="s2">&#34;text&#34;</span><span class="p">)</span>  <span class="c1"># Extract text from the page</span>
</span></span><span class="line"><span class="cl">        <span class="n">all_text</span> <span class="o">+=</span> <span class="n">text</span>  <span class="c1"># Append the extracted text to the all_text string</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">all_text</span>  <span class="c1"># Return the extracted text</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">chunk_text</span><span class="p">(</span><span class="n">text</span><span class="p">,</span> <span class="n">n</span><span class="p">,</span> <span class="n">overlap</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;
</span></span></span><span class="line"><span class="cl"><span class="s2">    Chunks the given text into segments of n characters with overlap.
</span></span></span><span class="line"><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="cl"><span class="s2">    Args:
</span></span></span><span class="line"><span class="cl"><span class="s2">    text (str): The text to be chunked.
</span></span></span><span class="line"><span class="cl"><span class="s2">    n (int): The number of characters in each chunk.
</span></span></span><span class="line"><span class="cl"><span class="s2">    overlap (int): The number of overlapping characters between chunks.
</span></span></span><span class="line"><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="cl"><span class="s2">    Returns:
</span></span></span><span class="line"><span class="cl"><span class="s2">    List[str]: A list of text chunks.
</span></span></span><span class="line"><span class="cl"><span class="s2">    &#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">chunks</span> <span class="o">=</span> <span class="p">[]</span>  <span class="c1"># Initialize an empty list to store the chunks</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># Loop through the text with a step size of (n - overlap)</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">text</span><span class="p">),</span> <span class="n">n</span> <span class="o">-</span> <span class="n">overlap</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="c1"># Append a chunk of text from index i to i + n to the chunks list</span>
</span></span><span class="line"><span class="cl">        <span class="n">chunks</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">text</span><span class="p">[</span><span class="n">i</span><span class="p">:</span><span class="n">i</span> <span class="o">+</span> <span class="n">n</span><span class="p">])</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">chunks</span>  <span class="c1"># Return the list of text chunks</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Initialize the OpenAI client with the base URL and API key</span>
</span></span><span class="line"><span class="cl"><span class="n">client</span> <span class="o">=</span> <span class="n">OpenAI</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">base_url</span><span class="o">=</span><span class="s2">&#34;https://api.studio.nebius.com/v1/&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">api_key</span><span class="o">=</span><span class="n">os</span><span class="o">.</span><span class="n">getenv</span><span class="p">(</span><span class="s2">&#34;OPENAI_API_KEY&#34;</span><span class="p">)</span>  <span class="c1"># Retrieve the API key from environment variables</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">create_embeddings</span><span class="p">(</span><span class="n">text</span><span class="p">,</span> <span class="n">model</span><span class="o">=</span><span class="s2">&#34;BAAI/bge-en-icl&#34;</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;
</span></span></span><span class="line"><span class="cl"><span class="s2">    Creates embeddings for the given text using the specified OpenAI model.
</span></span></span><span class="line"><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="cl"><span class="s2">    Args:
</span></span></span><span class="line"><span class="cl"><span class="s2">    text (str): The input text for which embeddings are to be created.
</span></span></span><span class="line"><span class="cl"><span class="s2">    model (str): The model to be used for creating embeddings. Default is &#34;BAAI/bge-en-icl&#34;.
</span></span></span><span class="line"><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="cl"><span class="s2">    Returns:
</span></span></span><span class="line"><span class="cl"><span class="s2">    dict: The response from the OpenAI API containing the embeddings.
</span></span></span><span class="line"><span class="cl"><span class="s2">    &#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># Create embeddings for the input text using the specified model</span>
</span></span><span class="line"><span class="cl">    <span class="n">response</span> <span class="o">=</span> <span class="n">client</span><span class="o">.</span><span class="n">embeddings</span><span class="o">.</span><span class="n">create</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="n">model</span><span class="o">=</span><span class="n">model</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nb">input</span><span class="o">=</span><span class="n">text</span>
</span></span><span class="line"><span class="cl">    <span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">response</span>  <span class="c1"># Return the response containing the embeddings</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">cosine_similarity</span><span class="p">(</span><span class="n">vec1</span><span class="p">,</span> <span class="n">vec2</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;
</span></span></span><span class="line"><span class="cl"><span class="s2">    Calculates the cosine similarity between two vectors.
</span></span></span><span class="line"><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="cl"><span class="s2">    Args:
</span></span></span><span class="line"><span class="cl"><span class="s2">    vec1 (np.ndarray): The first vector.
</span></span></span><span class="line"><span class="cl"><span class="s2">    vec2 (np.ndarray): The second vector.
</span></span></span><span class="line"><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="cl"><span class="s2">    Returns:
</span></span></span><span class="line"><span class="cl"><span class="s2">    float: The cosine similarity between the two vectors.
</span></span></span><span class="line"><span class="cl"><span class="s2">    &#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># Compute the dot product of the two vectors and divide by the product of their norms</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">np</span><span class="o">.</span><span class="n">dot</span><span class="p">(</span><span class="n">vec1</span><span class="p">,</span> <span class="n">vec2</span><span class="p">)</span> <span class="o">/</span> <span class="p">(</span><span class="n">np</span><span class="o">.</span><span class="n">linalg</span><span class="o">.</span><span class="n">norm</span><span class="p">(</span><span class="n">vec1</span><span class="p">)</span> <span class="o">*</span> <span class="n">np</span><span class="o">.</span><span class="n">linalg</span><span class="o">.</span><span class="n">norm</span><span class="p">(</span><span class="n">vec2</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">semantic_search</span><span class="p">(</span><span class="n">query</span><span class="p">,</span> <span class="n">text_chunks</span><span class="p">,</span> <span class="n">embeddings</span><span class="p">,</span> <span class="n">k</span><span class="o">=</span><span class="mi">5</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;
</span></span></span><span class="line"><span class="cl"><span class="s2">    Performs semantic search on the text chunks using the given query and embeddings.
</span></span></span><span class="line"><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="cl"><span class="s2">    Args:
</span></span></span><span class="line"><span class="cl"><span class="s2">    query (str): The query for the semantic search.
</span></span></span><span class="line"><span class="cl"><span class="s2">    text_chunks (List[str]): A list of text chunks to search through.
</span></span></span><span class="line"><span class="cl"><span class="s2">    embeddings (List[dict]): A list of embeddings for the text chunks.
</span></span></span><span class="line"><span class="cl"><span class="s2">    k (int): The number of top relevant text chunks to return. Default is 5.
</span></span></span><span class="line"><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="cl"><span class="s2">    Returns:
</span></span></span><span class="line"><span class="cl"><span class="s2">    List[str]: A list of the top k most relevant text chunks based on the query.
</span></span></span><span class="line"><span class="cl"><span class="s2">    &#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># Create an embedding for the query</span>
</span></span><span class="line"><span class="cl">    <span class="n">query_embedding</span> <span class="o">=</span> <span class="n">create_embeddings</span><span class="p">(</span><span class="n">query</span><span class="p">)</span><span class="o">.</span><span class="n">data</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">.</span><span class="n">embedding</span>
</span></span><span class="line"><span class="cl">    <span class="n">similarity_scores</span> <span class="o">=</span> <span class="p">[]</span>  <span class="c1"># Initialize a list to store similarity scores</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># Calculate similarity scores between the query embedding and each text chunk embedding</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="n">i</span><span class="p">,</span> <span class="n">chunk_embedding</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">embeddings</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="n">similarity_score</span> <span class="o">=</span> <span class="n">cosine_similarity</span><span class="p">(</span><span class="n">np</span><span class="o">.</span><span class="n">array</span><span class="p">(</span><span class="n">query_embedding</span><span class="p">),</span> <span class="n">np</span><span class="o">.</span><span class="n">array</span><span class="p">(</span><span class="n">chunk_embedding</span><span class="o">.</span><span class="n">embedding</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">        <span class="n">similarity_scores</span><span class="o">.</span><span class="n">append</span><span class="p">((</span><span class="n">i</span><span class="p">,</span> <span class="n">similarity_score</span><span class="p">))</span>  <span class="c1"># Append the index and similarity score</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># Sort the similarity scores in descending order</span>
</span></span><span class="line"><span class="cl">    <span class="n">similarity_scores</span><span class="o">.</span><span class="n">sort</span><span class="p">(</span><span class="n">key</span><span class="o">=</span><span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="n">x</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="n">reverse</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># Get the indices of the top k most similar text chunks</span>
</span></span><span class="line"><span class="cl">    <span class="n">top_indices</span> <span class="o">=</span> <span class="p">[</span><span class="n">index</span> <span class="k">for</span> <span class="n">index</span><span class="p">,</span> <span class="n">_</span> <span class="ow">in</span> <span class="n">similarity_scores</span><span class="p">[:</span><span class="n">k</span><span class="p">]]</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># Return the top k most relevant text chunks</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="p">[</span><span class="n">text_chunks</span><span class="p">[</span><span class="n">index</span><span class="p">]</span> <span class="k">for</span> <span class="n">index</span> <span class="ow">in</span> <span class="n">top_indices</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">generate_response</span><span class="p">(</span><span class="n">system_prompt</span><span class="p">,</span> <span class="n">user_message</span><span class="p">,</span> <span class="n">model</span><span class="o">=</span><span class="s2">&#34;meta-llama/Llama-3.2-3B-Instruct&#34;</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;
</span></span></span><span class="line"><span class="cl"><span class="s2">    Generates a response from the AI model based on the system prompt and user message.
</span></span></span><span class="line"><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="cl"><span class="s2">    Args:
</span></span></span><span class="line"><span class="cl"><span class="s2">    system_prompt (str): The system prompt to guide the AI&#39;s behavior.
</span></span></span><span class="line"><span class="cl"><span class="s2">    user_message (str): The user&#39;s message or query.
</span></span></span><span class="line"><span class="cl"><span class="s2">    model (str): The model to be used for generating the response. Default is &#34;meta-llama/Llama-2-7B-chat-hf&#34;.
</span></span></span><span class="line"><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="cl"><span class="s2">    Returns:
</span></span></span><span class="line"><span class="cl"><span class="s2">    dict: The response from the AI model.
</span></span></span><span class="line"><span class="cl"><span class="s2">    &#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">response</span> <span class="o">=</span> <span class="n">client</span><span class="o">.</span><span class="n">chat</span><span class="o">.</span><span class="n">completions</span><span class="o">.</span><span class="n">create</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="n">model</span><span class="o">=</span><span class="n">model</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">temperature</span><span class="o">=</span><span class="mi">0</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">messages</span><span class="o">=</span><span class="p">[</span>
</span></span><span class="line"><span class="cl">            <span class="p">{</span><span class="s2">&#34;role&#34;</span><span class="p">:</span> <span class="s2">&#34;system&#34;</span><span class="p">,</span> <span class="s2">&#34;content&#34;</span><span class="p">:</span> <span class="n">system_prompt</span><span class="p">},</span>
</span></span><span class="line"><span class="cl">            <span class="p">{</span><span class="s2">&#34;role&#34;</span><span class="p">:</span> <span class="s2">&#34;user&#34;</span><span class="p">,</span> <span class="s2">&#34;content&#34;</span><span class="p">:</span> <span class="n">user_message</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">]</span>
</span></span><span class="line"><span class="cl">    <span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">response</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 完整调用流程</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">simple_rag_pipeline</span><span class="p">(</span><span class="n">pdf_path</span><span class="p">,</span> <span class="n">query</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># 1. 提取PDF文本</span>
</span></span><span class="line"><span class="cl">    <span class="n">extracted_text</span> <span class="o">=</span> <span class="n">extract_text_from_pdf</span><span class="p">(</span><span class="n">pdf_path</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># 2. 分块处理</span>
</span></span><span class="line"><span class="cl">    <span class="n">text_chunks</span> <span class="o">=</span> <span class="n">chunk_text</span><span class="p">(</span><span class="n">extracted_text</span><span class="p">,</span> <span class="mi">1000</span><span class="p">,</span> <span class="mi">200</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># 3. 创建嵌入</span>
</span></span><span class="line"><span class="cl">    <span class="n">response</span> <span class="o">=</span> <span class="n">create_embeddings</span><span class="p">(</span><span class="n">text_chunks</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># 4. 语义搜索</span>
</span></span><span class="line"><span class="cl">    <span class="n">top_chunks</span> <span class="o">=</span> <span class="n">semantic_search</span><span class="p">(</span><span class="n">query</span><span class="p">,</span> <span class="n">text_chunks</span><span class="p">,</span> <span class="n">response</span><span class="o">.</span><span class="n">data</span><span class="p">,</span> <span class="n">k</span><span class="o">=</span><span class="mi">2</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># 5. 生成回答</span>
</span></span><span class="line"><span class="cl">    <span class="n">system_prompt</span> <span class="o">=</span> <span class="s2">&#34;You are an AI assistant that strictly answers based on the given context. If the answer cannot be derived directly from the provided context, respond with: &#39;I do not have enough information to answer that.&#39;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">user_prompt</span> <span class="o">=</span> <span class="s2">&#34;</span><span class="se">\n</span><span class="s2">&#34;</span><span class="o">.</span><span class="n">join</span><span class="p">([</span><span class="sa">f</span><span class="s2">&#34;Context </span><span class="si">{</span><span class="n">i</span> <span class="o">+</span> <span class="mi">1</span><span class="si">}</span><span class="s2">:</span><span class="se">\n</span><span class="si">{</span><span class="n">chunk</span><span class="si">}</span><span class="se">\n</span><span class="s2">=====================================</span><span class="se">\n</span><span class="s2">&#34;</span> <span class="k">for</span> <span class="n">i</span><span class="p">,</span> <span class="n">chunk</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">top_chunks</span><span class="p">)])</span>
</span></span><span class="line"><span class="cl">    <span class="n">user_prompt</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">&#34;</span><span class="si">{</span><span class="n">user_prompt</span><span class="si">}</span><span class="se">\n</span><span class="s2">Question: </span><span class="si">{</span><span class="n">query</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">ai_response</span> <span class="o">=</span> <span class="n">generate_response</span><span class="p">(</span><span class="n">system_prompt</span><span class="p">,</span> <span class="n">user_prompt</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">ai_response</span><span class="o">.</span><span class="n">choices</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">.</span><span class="n">message</span><span class="o">.</span><span class="n">content</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="代码讲解">代码讲解</h2>
<ul>
<li><strong>文档处理</strong>：使用PyMuPDF提取PDF文本，按字符数分块</li>
<li><strong>嵌入生成</strong>：使用BAAI/bge-en-icl模型生成文本嵌入</li>
<li><strong>语义搜索</strong>：计算查询与文档块的余弦相似度，返回最相关的k个片段</li>
<li><strong>答案生成</strong>：将检索到的上下文与用户问题输入LLM生成答案</li>
</ul>
<h2 id="主要特点">主要特点</h2>
<ul>
<li>实现简单，易于理解和扩展</li>
<li>使用余弦相似度进行语义检索</li>
<li>支持PDF文档处理</li>
<li>可配置的检索数量k</li>
</ul>
<h2 id="使用场景">使用场景</h2>
<ul>
<li>FAQ自动问答</li>
<li>小型企业知识库</li>
<li>结构化文档检索增强</li>
<li>基础文档问答系统</li>
</ul>
]]></description></item><item><title>RAG系列-语义分块RAG（Semantic Chunking RAG）</title><link>https://blog.ethan-ai.cn/%E8%AF%AD%E4%B9%89%E5%88%86%E5%9D%97rag/</link><pubDate>Wed, 18 Jun 2025 00:00:00 +0800</pubDate><author>作者</author><guid>https://blog.ethan-ai.cn/%E8%AF%AD%E4%B9%89%E5%88%86%E5%9D%97rag/</guid><description><![CDATA[<h1 id="02-语义分块ragsemantic-chunking-rag">02. 语义分块RAG（Semantic Chunking RAG）</h1>
<h2 id="方法简介">方法简介</h2>
<p>语义分块RAG通过计算句子间的语义相似度来智能分块，而不是简单的固定长度分块。它使用百分位数、标准差或四分位距等方法找到语义断点，将文本分割成语义连贯的块，提升检索精度。</p>
<h2 id="核心代码">核心代码</h2>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">  1
</span><span class="lnt">  2
</span><span class="lnt">  3
</span><span class="lnt">  4
</span><span class="lnt">  5
</span><span class="lnt">  6
</span><span class="lnt">  7
</span><span class="lnt">  8
</span><span class="lnt">  9
</span><span class="lnt"> 10
</span><span class="lnt"> 11
</span><span class="lnt"> 12
</span><span class="lnt"> 13
</span><span class="lnt"> 14
</span><span class="lnt"> 15
</span><span class="lnt"> 16
</span><span class="lnt"> 17
</span><span class="lnt"> 18
</span><span class="lnt"> 19
</span><span class="lnt"> 20
</span><span class="lnt"> 21
</span><span class="lnt"> 22
</span><span class="lnt"> 23
</span><span class="lnt"> 24
</span><span class="lnt"> 25
</span><span class="lnt"> 26
</span><span class="lnt"> 27
</span><span class="lnt"> 28
</span><span class="lnt"> 29
</span><span class="lnt"> 30
</span><span class="lnt"> 31
</span><span class="lnt"> 32
</span><span class="lnt"> 33
</span><span class="lnt"> 34
</span><span class="lnt"> 35
</span><span class="lnt"> 36
</span><span class="lnt"> 37
</span><span class="lnt"> 38
</span><span class="lnt"> 39
</span><span class="lnt"> 40
</span><span class="lnt"> 41
</span><span class="lnt"> 42
</span><span class="lnt"> 43
</span><span class="lnt"> 44
</span><span class="lnt"> 45
</span><span class="lnt"> 46
</span><span class="lnt"> 47
</span><span class="lnt"> 48
</span><span class="lnt"> 49
</span><span class="lnt"> 50
</span><span class="lnt"> 51
</span><span class="lnt"> 52
</span><span class="lnt"> 53
</span><span class="lnt"> 54
</span><span class="lnt"> 55
</span><span class="lnt"> 56
</span><span class="lnt"> 57
</span><span class="lnt"> 58
</span><span class="lnt"> 59
</span><span class="lnt"> 60
</span><span class="lnt"> 61
</span><span class="lnt"> 62
</span><span class="lnt"> 63
</span><span class="lnt"> 64
</span><span class="lnt"> 65
</span><span class="lnt"> 66
</span><span class="lnt"> 67
</span><span class="lnt"> 68
</span><span class="lnt"> 69
</span><span class="lnt"> 70
</span><span class="lnt"> 71
</span><span class="lnt"> 72
</span><span class="lnt"> 73
</span><span class="lnt"> 74
</span><span class="lnt"> 75
</span><span class="lnt"> 76
</span><span class="lnt"> 77
</span><span class="lnt"> 78
</span><span class="lnt"> 79
</span><span class="lnt"> 80
</span><span class="lnt"> 81
</span><span class="lnt"> 82
</span><span class="lnt"> 83
</span><span class="lnt"> 84
</span><span class="lnt"> 85
</span><span class="lnt"> 86
</span><span class="lnt"> 87
</span><span class="lnt"> 88
</span><span class="lnt"> 89
</span><span class="lnt"> 90
</span><span class="lnt"> 91
</span><span class="lnt"> 92
</span><span class="lnt"> 93
</span><span class="lnt"> 94
</span><span class="lnt"> 95
</span><span class="lnt"> 96
</span><span class="lnt"> 97
</span><span class="lnt"> 98
</span><span class="lnt"> 99
</span><span class="lnt">100
</span><span class="lnt">101
</span><span class="lnt">102
</span><span class="lnt">103
</span><span class="lnt">104
</span><span class="lnt">105
</span><span class="lnt">106
</span><span class="lnt">107
</span><span class="lnt">108
</span><span class="lnt">109
</span><span class="lnt">110
</span><span class="lnt">111
</span><span class="lnt">112
</span><span class="lnt">113
</span><span class="lnt">114
</span><span class="lnt">115
</span><span class="lnt">116
</span><span class="lnt">117
</span><span class="lnt">118
</span><span class="lnt">119
</span><span class="lnt">120
</span><span class="lnt">121
</span><span class="lnt">122
</span><span class="lnt">123
</span><span class="lnt">124
</span><span class="lnt">125
</span><span class="lnt">126
</span><span class="lnt">127
</span><span class="lnt">128
</span><span class="lnt">129
</span><span class="lnt">130
</span><span class="lnt">131
</span><span class="lnt">132
</span><span class="lnt">133
</span><span class="lnt">134
</span><span class="lnt">135
</span><span class="lnt">136
</span><span class="lnt">137
</span><span class="lnt">138
</span><span class="lnt">139
</span><span class="lnt">140
</span><span class="lnt">141
</span><span class="lnt">142
</span><span class="lnt">143
</span><span class="lnt">144
</span><span class="lnt">145
</span><span class="lnt">146
</span><span class="lnt">147
</span><span class="lnt">148
</span><span class="lnt">149
</span><span class="lnt">150
</span><span class="lnt">151
</span><span class="lnt">152
</span><span class="lnt">153
</span><span class="lnt">154
</span><span class="lnt">155
</span><span class="lnt">156
</span><span class="lnt">157
</span><span class="lnt">158
</span><span class="lnt">159
</span><span class="lnt">160
</span><span class="lnt">161
</span><span class="lnt">162
</span><span class="lnt">163
</span><span class="lnt">164
</span><span class="lnt">165
</span><span class="lnt">166
</span><span class="lnt">167
</span><span class="lnt">168
</span><span class="lnt">169
</span><span class="lnt">170
</span><span class="lnt">171
</span><span class="lnt">172
</span><span class="lnt">173
</span><span class="lnt">174
</span><span class="lnt">175
</span><span class="lnt">176
</span><span class="lnt">177
</span><span class="lnt">178
</span><span class="lnt">179
</span><span class="lnt">180
</span><span class="lnt">181
</span><span class="lnt">182
</span><span class="lnt">183
</span><span class="lnt">184
</span><span class="lnt">185
</span><span class="lnt">186
</span><span class="lnt">187
</span><span class="lnt">188
</span><span class="lnt">189
</span><span class="lnt">190
</span><span class="lnt">191
</span><span class="lnt">192
</span><span class="lnt">193
</span><span class="lnt">194
</span><span class="lnt">195
</span><span class="lnt">196
</span><span class="lnt">197
</span><span class="lnt">198
</span><span class="lnt">199
</span><span class="lnt">200
</span><span class="lnt">201
</span><span class="lnt">202
</span><span class="lnt">203
</span><span class="lnt">204
</span><span class="lnt">205
</span><span class="lnt">206
</span><span class="lnt">207
</span><span class="lnt">208
</span><span class="lnt">209
</span><span class="lnt">210
</span><span class="lnt">211
</span><span class="lnt">212
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">fitz</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">os</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">numpy</span> <span class="k">as</span> <span class="nn">np</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">json</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">openai</span> <span class="kn">import</span> <span class="n">OpenAI</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">extract_text_from_pdf</span><span class="p">(</span><span class="n">pdf_path</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;
</span></span></span><span class="line"><span class="cl"><span class="s2">    Extracts text from a PDF file.
</span></span></span><span class="line"><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="cl"><span class="s2">    Args:
</span></span></span><span class="line"><span class="cl"><span class="s2">    pdf_path (str): Path to the PDF file.
</span></span></span><span class="line"><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="cl"><span class="s2">    Returns:
</span></span></span><span class="line"><span class="cl"><span class="s2">    str: Extracted text from the PDF.
</span></span></span><span class="line"><span class="cl"><span class="s2">    &#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># Open the PDF file</span>
</span></span><span class="line"><span class="cl">    <span class="n">mypdf</span> <span class="o">=</span> <span class="n">fitz</span><span class="o">.</span><span class="n">open</span><span class="p">(</span><span class="n">pdf_path</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">all_text</span> <span class="o">=</span> <span class="s2">&#34;&#34;</span>  <span class="c1"># Initialize an empty string to store the extracted text</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># Iterate through each page in the PDF</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="n">page</span> <span class="ow">in</span> <span class="n">mypdf</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="c1"># Extract text from the current page and add spacing</span>
</span></span><span class="line"><span class="cl">        <span class="n">all_text</span> <span class="o">+=</span> <span class="n">page</span><span class="o">.</span><span class="n">get_text</span><span class="p">(</span><span class="s2">&#34;text&#34;</span><span class="p">)</span> <span class="o">+</span> <span class="s2">&#34; &#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># Return the extracted text, stripped of leading/trailing whitespace</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">all_text</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Initialize the OpenAI client with the base URL and API key</span>
</span></span><span class="line"><span class="cl"><span class="n">client</span> <span class="o">=</span> <span class="n">OpenAI</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">base_url</span><span class="o">=</span><span class="s2">&#34;https://api.studio.nebius.com/v1/&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">api_key</span><span class="o">=</span><span class="n">os</span><span class="o">.</span><span class="n">getenv</span><span class="p">(</span><span class="s2">&#34;OPENAI_API_KEY&#34;</span><span class="p">)</span>  <span class="c1"># Retrieve the API key from environment variables</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">get_embedding</span><span class="p">(</span><span class="n">text</span><span class="p">,</span> <span class="n">model</span><span class="o">=</span><span class="s2">&#34;BAAI/bge-en-icl&#34;</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;
</span></span></span><span class="line"><span class="cl"><span class="s2">    Creates an embedding for the given text using OpenAI.
</span></span></span><span class="line"><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="cl"><span class="s2">    Args:
</span></span></span><span class="line"><span class="cl"><span class="s2">    text (str): Input text.
</span></span></span><span class="line"><span class="cl"><span class="s2">    model (str): Embedding model name.
</span></span></span><span class="line"><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="cl"><span class="s2">    Returns:
</span></span></span><span class="line"><span class="cl"><span class="s2">    np.ndarray: The embedding vector.
</span></span></span><span class="line"><span class="cl"><span class="s2">    &#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">response</span> <span class="o">=</span> <span class="n">client</span><span class="o">.</span><span class="n">embeddings</span><span class="o">.</span><span class="n">create</span><span class="p">(</span><span class="n">model</span><span class="o">=</span><span class="n">model</span><span class="p">,</span> <span class="nb">input</span><span class="o">=</span><span class="n">text</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">np</span><span class="o">.</span><span class="n">array</span><span class="p">(</span><span class="n">response</span><span class="o">.</span><span class="n">data</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">.</span><span class="n">embedding</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">cosine_similarity</span><span class="p">(</span><span class="n">vec1</span><span class="p">,</span> <span class="n">vec2</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;
</span></span></span><span class="line"><span class="cl"><span class="s2">    Computes cosine similarity between two vectors.
</span></span></span><span class="line"><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="cl"><span class="s2">    Args:
</span></span></span><span class="line"><span class="cl"><span class="s2">    vec1 (np.ndarray): First vector.
</span></span></span><span class="line"><span class="cl"><span class="s2">    vec2 (np.ndarray): Second vector.
</span></span></span><span class="line"><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="cl"><span class="s2">    Returns:
</span></span></span><span class="line"><span class="cl"><span class="s2">    float: Cosine similarity.
</span></span></span><span class="line"><span class="cl"><span class="s2">    &#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">np</span><span class="o">.</span><span class="n">dot</span><span class="p">(</span><span class="n">vec1</span><span class="p">,</span> <span class="n">vec2</span><span class="p">)</span> <span class="o">/</span> <span class="p">(</span><span class="n">np</span><span class="o">.</span><span class="n">linalg</span><span class="o">.</span><span class="n">norm</span><span class="p">(</span><span class="n">vec1</span><span class="p">)</span> <span class="o">*</span> <span class="n">np</span><span class="o">.</span><span class="n">linalg</span><span class="o">.</span><span class="n">norm</span><span class="p">(</span><span class="n">vec2</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">compute_breakpoints</span><span class="p">(</span><span class="n">similarities</span><span class="p">,</span> <span class="n">method</span><span class="o">=</span><span class="s2">&#34;percentile&#34;</span><span class="p">,</span> <span class="n">threshold</span><span class="o">=</span><span class="mi">90</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;
</span></span></span><span class="line"><span class="cl"><span class="s2">    Computes chunking breakpoints based on similarity drops.
</span></span></span><span class="line"><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="cl"><span class="s2">    Args:
</span></span></span><span class="line"><span class="cl"><span class="s2">    similarities (List[float]): List of similarity scores between sentences.
</span></span></span><span class="line"><span class="cl"><span class="s2">    method (str): &#39;percentile&#39;, &#39;standard_deviation&#39;, or &#39;interquartile&#39;.
</span></span></span><span class="line"><span class="cl"><span class="s2">    threshold (float): Threshold value (percentile for &#39;percentile&#39;, std devs for &#39;standard_deviation&#39;).
</span></span></span><span class="line"><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="cl"><span class="s2">    Returns:
</span></span></span><span class="line"><span class="cl"><span class="s2">    List[int]: Indices where chunk splits should occur.
</span></span></span><span class="line"><span class="cl"><span class="s2">    &#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># Determine the threshold value based on the selected method</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">method</span> <span class="o">==</span> <span class="s2">&#34;percentile&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="c1"># Calculate the Xth percentile of the similarity scores</span>
</span></span><span class="line"><span class="cl">        <span class="n">threshold_value</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">percentile</span><span class="p">(</span><span class="n">similarities</span><span class="p">,</span> <span class="n">threshold</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">elif</span> <span class="n">method</span> <span class="o">==</span> <span class="s2">&#34;standard_deviation&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="c1"># Calculate the mean and standard deviation of the similarity scores</span>
</span></span><span class="line"><span class="cl">        <span class="n">mean</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">mean</span><span class="p">(</span><span class="n">similarities</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">std_dev</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">std</span><span class="p">(</span><span class="n">similarities</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="c1"># Set the threshold value to mean minus X standard deviations</span>
</span></span><span class="line"><span class="cl">        <span class="n">threshold_value</span> <span class="o">=</span> <span class="n">mean</span> <span class="o">-</span> <span class="p">(</span><span class="n">threshold</span> <span class="o">*</span> <span class="n">std_dev</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">elif</span> <span class="n">method</span> <span class="o">==</span> <span class="s2">&#34;interquartile&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="c1"># Calculate the first and third quartiles (Q1 and Q3)</span>
</span></span><span class="line"><span class="cl">        <span class="n">q1</span><span class="p">,</span> <span class="n">q3</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">percentile</span><span class="p">(</span><span class="n">similarities</span><span class="p">,</span> <span class="p">[</span><span class="mi">25</span><span class="p">,</span> <span class="mi">75</span><span class="p">])</span>
</span></span><span class="line"><span class="cl">        <span class="c1"># Set the threshold value using the IQR rule for outliers</span>
</span></span><span class="line"><span class="cl">        <span class="n">threshold_value</span> <span class="o">=</span> <span class="n">q1</span> <span class="o">-</span> <span class="mf">1.5</span> <span class="o">*</span> <span class="p">(</span><span class="n">q3</span> <span class="o">-</span> <span class="n">q1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="c1"># Raise an error if an invalid method is provided</span>
</span></span><span class="line"><span class="cl">        <span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="s2">&#34;Invalid method. Choose &#39;percentile&#39;, &#39;standard_deviation&#39;, or &#39;interquartile&#39;.&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># Identify indices where similarity drops below the threshold value</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="p">[</span><span class="n">i</span> <span class="k">for</span> <span class="n">i</span><span class="p">,</span> <span class="n">sim</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">similarities</span><span class="p">)</span> <span class="k">if</span> <span class="n">sim</span> <span class="o">&lt;</span> <span class="n">threshold_value</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">split_into_chunks</span><span class="p">(</span><span class="n">sentences</span><span class="p">,</span> <span class="n">breakpoints</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;
</span></span></span><span class="line"><span class="cl"><span class="s2">    Splits sentences into semantic chunks.
</span></span></span><span class="line"><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="cl"><span class="s2">    Args:
</span></span></span><span class="line"><span class="cl"><span class="s2">    sentences (List[str]): List of sentences.
</span></span></span><span class="line"><span class="cl"><span class="s2">    breakpoints (List[int]): Indices where chunking should occur.
</span></span></span><span class="line"><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="cl"><span class="s2">    Returns:
</span></span></span><span class="line"><span class="cl"><span class="s2">    List[str]: List of text chunks.
</span></span></span><span class="line"><span class="cl"><span class="s2">    &#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">chunks</span> <span class="o">=</span> <span class="p">[]</span>  <span class="c1"># Initialize an empty list to store the chunks</span>
</span></span><span class="line"><span class="cl">    <span class="n">start</span> <span class="o">=</span> <span class="mi">0</span>  <span class="c1"># Initialize the start index</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># Iterate through each breakpoint to create chunks</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="n">bp</span> <span class="ow">in</span> <span class="n">breakpoints</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="c1"># Append the chunk of sentences from start to the current breakpoint</span>
</span></span><span class="line"><span class="cl">        <span class="n">chunks</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="s2">&#34;. &#34;</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">sentences</span><span class="p">[</span><span class="n">start</span><span class="p">:</span><span class="n">bp</span> <span class="o">+</span> <span class="mi">1</span><span class="p">])</span> <span class="o">+</span> <span class="s2">&#34;.&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">start</span> <span class="o">=</span> <span class="n">bp</span> <span class="o">+</span> <span class="mi">1</span>  <span class="c1"># Update the start index to the next sentence after the breakpoint</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># Append the remaining sentences as the last chunk</span>
</span></span><span class="line"><span class="cl">    <span class="n">chunks</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="s2">&#34;. &#34;</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">sentences</span><span class="p">[</span><span class="n">start</span><span class="p">:]))</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">chunks</span>  <span class="c1"># Return the list of chunks</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">create_embeddings</span><span class="p">(</span><span class="n">text_chunks</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;
</span></span></span><span class="line"><span class="cl"><span class="s2">    Creates embeddings for each text chunk.
</span></span></span><span class="line"><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="cl"><span class="s2">    Args:
</span></span></span><span class="line"><span class="cl"><span class="s2">    text_chunks (List[str]): List of text chunks.
</span></span></span><span class="line"><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="cl"><span class="s2">    Returns:
</span></span></span><span class="line"><span class="cl"><span class="s2">    List[np.ndarray]: List of embedding vectors.
</span></span></span><span class="line"><span class="cl"><span class="s2">    &#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># Generate embeddings for each text chunk using the get_embedding function</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="p">[</span><span class="n">get_embedding</span><span class="p">(</span><span class="n">chunk</span><span class="p">)</span> <span class="k">for</span> <span class="n">chunk</span> <span class="ow">in</span> <span class="n">text_chunks</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">semantic_search</span><span class="p">(</span><span class="n">query</span><span class="p">,</span> <span class="n">text_chunks</span><span class="p">,</span> <span class="n">chunk_embeddings</span><span class="p">,</span> <span class="n">k</span><span class="o">=</span><span class="mi">5</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;
</span></span></span><span class="line"><span class="cl"><span class="s2">    Finds the most relevant text chunks for a query.
</span></span></span><span class="line"><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="cl"><span class="s2">    Args:
</span></span></span><span class="line"><span class="cl"><span class="s2">    query (str): Search query.
</span></span></span><span class="line"><span class="cl"><span class="s2">    text_chunks (List[str]): List of text chunks.
</span></span></span><span class="line"><span class="cl"><span class="s2">    chunk_embeddings (List[np.ndarray]): List of chunk embeddings.
</span></span></span><span class="line"><span class="cl"><span class="s2">    k (int): Number of top results to return.
</span></span></span><span class="line"><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="cl"><span class="s2">    Returns:
</span></span></span><span class="line"><span class="cl"><span class="s2">    List[str]: Top-k relevant chunks.
</span></span></span><span class="line"><span class="cl"><span class="s2">    &#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># Generate an embedding for the query</span>
</span></span><span class="line"><span class="cl">    <span class="n">query_embedding</span> <span class="o">=</span> <span class="n">get_embedding</span><span class="p">(</span><span class="n">query</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># Calculate cosine similarity between the query embedding and each chunk embedding</span>
</span></span><span class="line"><span class="cl">    <span class="n">similarities</span> <span class="o">=</span> <span class="p">[</span><span class="n">cosine_similarity</span><span class="p">(</span><span class="n">query_embedding</span><span class="p">,</span> <span class="n">emb</span><span class="p">)</span> <span class="k">for</span> <span class="n">emb</span> <span class="ow">in</span> <span class="n">chunk_embeddings</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># Get the indices of the top-k most similar chunks</span>
</span></span><span class="line"><span class="cl">    <span class="n">top_indices</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">argsort</span><span class="p">(</span><span class="n">similarities</span><span class="p">)[</span><span class="o">-</span><span class="n">k</span><span class="p">:][::</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># Return the top-k most relevant text chunks</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="p">[</span><span class="n">text_chunks</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="n">top_indices</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">generate_response</span><span class="p">(</span><span class="n">system_prompt</span><span class="p">,</span> <span class="n">user_message</span><span class="p">,</span> <span class="n">model</span><span class="o">=</span><span class="s2">&#34;meta-llama/Llama-3.2-3B-Instruct&#34;</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;
</span></span></span><span class="line"><span class="cl"><span class="s2">    Generates a response from the AI model based on the system prompt and user message.
</span></span></span><span class="line"><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="cl"><span class="s2">    Args:
</span></span></span><span class="line"><span class="cl"><span class="s2">    system_prompt (str): The system prompt to guide the AI&#39;s behavior.
</span></span></span><span class="line"><span class="cl"><span class="s2">    user_message (str): The user&#39;s message or query.
</span></span></span><span class="line"><span class="cl"><span class="s2">    model (str): The model to be used for generating the response. Default is &#34;meta-llama/Llama-2-7B-chat-hf&#34;.
</span></span></span><span class="line"><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="cl"><span class="s2">    Returns:
</span></span></span><span class="line"><span class="cl"><span class="s2">    dict: The response from the AI model.
</span></span></span><span class="line"><span class="cl"><span class="s2">    &#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">response</span> <span class="o">=</span> <span class="n">client</span><span class="o">.</span><span class="n">chat</span><span class="o">.</span><span class="n">completions</span><span class="o">.</span><span class="n">create</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="n">model</span><span class="o">=</span><span class="n">model</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">temperature</span><span class="o">=</span><span class="mi">0</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">messages</span><span class="o">=</span><span class="p">[</span>
</span></span><span class="line"><span class="cl">            <span class="p">{</span><span class="s2">&#34;role&#34;</span><span class="p">:</span> <span class="s2">&#34;system&#34;</span><span class="p">,</span> <span class="s2">&#34;content&#34;</span><span class="p">:</span> <span class="n">system_prompt</span><span class="p">},</span>
</span></span><span class="line"><span class="cl">            <span class="p">{</span><span class="s2">&#34;role&#34;</span><span class="p">:</span> <span class="s2">&#34;user&#34;</span><span class="p">,</span> <span class="s2">&#34;content&#34;</span><span class="p">:</span> <span class="n">user_message</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">]</span>
</span></span><span class="line"><span class="cl">    <span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">response</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 完整调用流程</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">semantic_chunking_rag_pipeline</span><span class="p">(</span><span class="n">pdf_path</span><span class="p">,</span> <span class="n">query</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># 1. 提取PDF文本</span>
</span></span><span class="line"><span class="cl">    <span class="n">extracted_text</span> <span class="o">=</span> <span class="n">extract_text_from_pdf</span><span class="p">(</span><span class="n">pdf_path</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># 2. 按句子分割</span>
</span></span><span class="line"><span class="cl">    <span class="n">sentences</span> <span class="o">=</span> <span class="n">extracted_text</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">&#34;. &#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># 3. 生成句子嵌入</span>
</span></span><span class="line"><span class="cl">    <span class="n">embeddings</span> <span class="o">=</span> <span class="p">[</span><span class="n">get_embedding</span><span class="p">(</span><span class="n">sentence</span><span class="p">)</span> <span class="k">for</span> <span class="n">sentence</span> <span class="ow">in</span> <span class="n">sentences</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># 4. 计算句子间相似度</span>
</span></span><span class="line"><span class="cl">    <span class="n">similarities</span> <span class="o">=</span> <span class="p">[</span><span class="n">cosine_similarity</span><span class="p">(</span><span class="n">embeddings</span><span class="p">[</span><span class="n">i</span><span class="p">],</span> <span class="n">embeddings</span><span class="p">[</span><span class="n">i</span> <span class="o">+</span> <span class="mi">1</span><span class="p">])</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">embeddings</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># 5. 计算断点（使用百分位数方法）</span>
</span></span><span class="line"><span class="cl">    <span class="n">breakpoints</span> <span class="o">=</span> <span class="n">compute_breakpoints</span><span class="p">(</span><span class="n">similarities</span><span class="p">,</span> <span class="n">method</span><span class="o">=</span><span class="s2">&#34;percentile&#34;</span><span class="p">,</span> <span class="n">threshold</span><span class="o">=</span><span class="mi">90</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># 6. 分割成语义块</span>
</span></span><span class="line"><span class="cl">    <span class="n">text_chunks</span> <span class="o">=</span> <span class="n">split_into_chunks</span><span class="p">(</span><span class="n">sentences</span><span class="p">,</span> <span class="n">breakpoints</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># 7. 创建块嵌入</span>
</span></span><span class="line"><span class="cl">    <span class="n">chunk_embeddings</span> <span class="o">=</span> <span class="n">create_embeddings</span><span class="p">(</span><span class="n">text_chunks</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># 8. 语义搜索</span>
</span></span><span class="line"><span class="cl">    <span class="n">top_chunks</span> <span class="o">=</span> <span class="n">semantic_search</span><span class="p">(</span><span class="n">query</span><span class="p">,</span> <span class="n">text_chunks</span><span class="p">,</span> <span class="n">chunk_embeddings</span><span class="p">,</span> <span class="n">k</span><span class="o">=</span><span class="mi">2</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># 9. 生成回答</span>
</span></span><span class="line"><span class="cl">    <span class="n">system_prompt</span> <span class="o">=</span> <span class="s2">&#34;You are an AI assistant that strictly answers based on the given context. If the answer cannot be derived directly from the provided context, respond with: &#39;I do not have enough information to answer that.&#39;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">user_prompt</span> <span class="o">=</span> <span class="s2">&#34;</span><span class="se">\n</span><span class="s2">&#34;</span><span class="o">.</span><span class="n">join</span><span class="p">([</span><span class="sa">f</span><span class="s2">&#34;Context </span><span class="si">{</span><span class="n">i</span> <span class="o">+</span> <span class="mi">1</span><span class="si">}</span><span class="s2">:</span><span class="se">\n</span><span class="si">{</span><span class="n">chunk</span><span class="si">}</span><span class="se">\n</span><span class="s2">=====================================</span><span class="se">\n</span><span class="s2">&#34;</span> <span class="k">for</span> <span class="n">i</span><span class="p">,</span> <span class="n">chunk</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">top_chunks</span><span class="p">)])</span>
</span></span><span class="line"><span class="cl">    <span class="n">user_prompt</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">&#34;</span><span class="si">{</span><span class="n">user_prompt</span><span class="si">}</span><span class="se">\n</span><span class="s2">Question: </span><span class="si">{</span><span class="n">query</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">ai_response</span> <span class="o">=</span> <span class="n">generate_response</span><span class="p">(</span><span class="n">system_prompt</span><span class="p">,</span> <span class="n">user_prompt</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">ai_response</span><span class="o">.</span><span class="n">choices</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">.</span><span class="n">message</span><span class="o">.</span><span class="n">content</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="代码讲解">代码讲解</h2>
<ul>
<li><strong>句子分割</strong>：按句号分割文本成句子</li>
<li><strong>嵌入生成</strong>：为每个句子生成向量表示</li>
<li><strong>相似度计算</strong>：计算相邻句子的余弦相似度</li>
<li><strong>断点检测</strong>：使用百分位数方法找到语义断点</li>
<li><strong>语义分块</strong>：根据断点将句子组合成语义块</li>
<li><strong>检索生成</strong>：基于语义块进行检索和答案生成</li>
</ul>
<h2 id="主要特点">主要特点</h2>
<ul>
<li>基于语义相似度的智能分块</li>
<li>支持多种断点检测方法（百分位数、标准差、四分位距）</li>
<li>保持语义连贯性</li>
<li>比固定长度分块更精准</li>
</ul>
<h2 id="使用场景">使用场景</h2>
<ul>
<li>长文档处理</li>
<li>需要保持语义完整性的场景</li>
<li>复杂问答系统</li>
<li>学术论文、技术文档等结构化文本</li>
</ul>
]]></description></item><item><title>2025年展望</title><link>https://blog.ethan-ai.cn/2025%E5%B9%B4%E5%B1%95%E6%9C%9B/</link><pubDate>Wed, 01 Jan 2025 00:00:00 +0800</pubDate><author>作者</author><guid>https://blog.ethan-ai.cn/2025%E5%B9%B4%E5%B1%95%E6%9C%9B/</guid><description><![CDATA[<h2 id="2024年回顾">2024年回顾</h2>
<h3 id="1月-2月安家落户">1月-2月：安家落户</h3>
<p>终于完成了人生中的一件大事 - 买房。拿到房产证的那一刻，我和妻子都感到无比欣喜。这个新家不仅是一个住所，更是我们对未来生活的美好期待。</p>
<h3 id="3月-4月平稳前行">3月-4月：平稳前行</h3>
<p>这段时间主要是日常工作和还贷。虽然每个月能存下的钱不多，但生活依然充满欢乐。我们学会了在有限的预算中寻找生活的乐趣。</p>
<h3 id="5月-6月职场动荡">5月-6月：职场动荡</h3>
<p>公司经历了裁员风波，虽然我幸免于难，但这次事件让我对公司的未来产生了疑虑。这段时间充满了迷茫和不确定性，尝试了很多事情但进展不大。</p>
<h3 id="7月-10月装修新家">7月-10月：装修新家</h3>
<p>新房进入装修阶段，虽然经济压力较大，但每周回长沙监工的过程充满了期待和喜悦。看着新家一点点成型，所有的辛苦都值得。</p>
<h3 id="11月-12月健康与ai探索">11月-12月：健康与AI探索</h3>
<p>体检发现患有桥本甲状腺炎，这让我开始更加关注身体健康。同时，AI技术的快速发展让我产生了强烈的危机感。经过深入思考和实践，我决定拥抱AI而不是恐惧它。</p>
<p>这段时间，我深入探索了多种AI工具：</p>
<ul>
<li>Cursor</li>
<li>Windsurf（开通了Pro版）</li>
<li>Cline</li>
<li>Aider</li>
<li>Zed AI</li>
</ul>
<p>通过实践，我发现每种工具都有其独特优势，于是开始尝试多种工具结合使用。12月中旬，我开始利用AI接一些外包项目，主要目的有两个：</p>
<ol>
<li>缓解经济压力</li>
<li>深入探索AI能力，提升工作效率</li>
</ol>
<h2 id="2025年展望">2025年展望</h2>
<p>在新的一年里，我为自己设定了以下目标：</p>
<ol>
<li><strong>AI SOP优化</strong>：总结出一套适合自己的AI使用流程和标准操作程序</li>
<li><strong>产品开发</strong>：借助AI工具，完成第一个产品的MVP（最小可行产品）</li>
<li><strong>全栈开发</strong>：开始探索全栈式开发，提升技术广度</li>
<li><strong>AI辅助学习</strong>：建立高效的AI辅助学习体系，加速知识获取</li>
<li><strong>事业基础</strong>：为未来的事业发展打下坚实基础</li>
</ol>
<p>2025年将是充满挑战和机遇的一年。我相信，通过合理利用AI工具，持续学习和自我提升，我能够实现这些目标，为未来创造更多可能性。</p>
<blockquote>
<p>&ldquo;未来属于那些相信梦想之美的人。&rdquo; - 埃莉诺·罗斯福</p>
</blockquote>
]]></description></item><item><title>windsurf编码体验</title><link>https://blog.ethan-ai.cn/windsurf%E7%BC%96%E7%A0%81%E4%BD%93%E9%AA%8C/</link><pubDate>Fri, 06 Dec 2024 17:32:26 +0800</pubDate><author>作者</author><guid>https://blog.ethan-ai.cn/windsurf%E7%BC%96%E7%A0%81%E4%BD%93%E9%AA%8C/</guid><description><![CDATA[<p>最近，我有幸体验了windsurf编辑器的AI辅助编程功能。这款编辑器的编程体验令人印象深刻，在某些方面甚至超越了我使用过的其他主流编辑器。然而，经过一天的深入使用后，我发现它仍存在一些需要改进的地方。</p>
<p>最初，我期望AI能够完全接管代码编写工作，或者至少大幅减少我在低级编码任务上的时间投入，让我能够专注于更高层次的架构设计。然而，现实与期望存在差距。在持续使用过程中，我注意到AI的解决问题的能力会随着会话时间的延长而逐渐下降。</p>
<p>经过分析，我认为这可能与windsurf的架构设计有关。该编辑器的AI辅助功能基于agent模式开发，在使用过程中我频繁遇到响应错误，且这种错误会随着使用时间的增加而变得更加频繁。更令人困扰的是，有时AI虽然能够正确回答问题，却无法实际修改代码来解决问题。我推测这可能是windsurf中存在的一个bug，导致AI无法持续执行代码修改任务。</p>
<p>值得注意的是，AI辅助编程的核心能力并非来自其&quot;智能&quot;，而是源于其对代码的深度分析和理解能力。AI之所以能够快速解决问题，是因为它能够高效地分析代码之间的关联，理解代码的逻辑结构和生命周期。然而，随着会话的持续，这种分析和理解能力似乎会逐渐衰减，这可能是由于软件性能优化不足导致的。</p>
<p>要实现真正优雅的AI辅助编程，还需要在多个方面进行优化和改进。虽然短期内可能无法完全解决这些问题，但不可否认的是，AI辅助编程仍然是一个强大的工具。它能够帮助我们快速学习和掌握新的编程语言，显著提升学习效率和工作效能。相信随着技术的不断进步，这些问题终将得到解决，为开发者带来更优质的编程体验。</p>
]]></description></item><item><title>读付鹏和高善文对当前经济评论</title><link>https://blog.ethan-ai.cn/%E8%AF%BB%E4%BB%98%E9%B9%8F%E5%92%8C%E9%AB%98%E5%96%84%E6%96%87%E5%AF%B9%E5%BD%93%E5%89%8D%E7%BB%8F%E6%B5%8E%E8%AF%84%E8%AE%BA/</link><pubDate>Tue, 03 Dec 2024 18:07:13 +0800</pubDate><author>作者</author><guid>https://blog.ethan-ai.cn/%E8%AF%BB%E4%BB%98%E9%B9%8F%E5%92%8C%E9%AB%98%E5%96%84%E6%96%87%E5%AF%B9%E5%BD%93%E5%89%8D%E7%BB%8F%E6%B5%8E%E8%AF%84%E8%AE%BA/</guid><description><![CDATA[<p>今天读了付鹏先生在HSBC内部演讲的文稿，后面相继听了高善文先生的演讲。在阅读了高善文和付鹏关于中国经济形势的深刻分析后，我获得了对当前和未来经济趋势的更全面理解。</p>
<p>通过深入阅读高善文的分析，我深刻认识到经济转型和周期性压力是塑造中国经济未来的关键力量。经济转型引发的结构性变化是深远而持久的，它要求我们不断适应新的发展模式，比如从劳动密集型向技术密集型转变，从投资驱动向消费驱动转变。与此同时，周期性压力则在短期内对经济产生显著影响，如需求波动、市场信心变化等，这些都可能对我们的职业和财务状况产生直接或间接的影响。</p>
<p>这种理解使我意识到在不同的经济周期阶段，需要采取不同的应对策略。目前，我们的职业生涯将长期处于这个经济周期的尾声阶段，这意味着整体经济环境、就业环境以及收入增长潜力都不能与经济高速发展时期同日而语。为了适应这些变化，我们需要认真评估自己所在行业在经济转型中的位置，以及未来可能的发展趋势。如果行业前景黯淡，可能需要考虑转行或提升技能以适应新兴行业的需求。在经济增速放缓的背景下，我们也需要更加谨慎地管理个人财务，包括减少不必要的债务、增加储蓄和投资于相对稳定的资产。然后调整消费习惯，避免过度消费，尤其是在经济不确定性较高时期，理性消费变得更加重要。也是时候考虑要开启副业，增加职业以外的收入，以应对可能的经济波动。希望能够更好地应对经济转型和周期性压力带来的挑战，同时也为未来可能出现的新机遇做好准备。</p>
<p>作为普通人，我们需要建立更为全面和深入的经济理解，以便在不断变化的经济环境中做出明智的决策。这两篇文章为我未来的财务规划和职业发展提供了宝贵的指导。</p>
]]></description></item><item><title>2024年第46周, 患上桥本了</title><link>https://blog.ethan-ai.cn/2024%E5%B9%B4%E7%AC%AC46%E5%91%A8/</link><pubDate>Thu, 14 Nov 2024 21:20:46 +0800</pubDate><author>作者</author><guid>https://blog.ethan-ai.cn/2024%E5%B9%B4%E7%AC%AC46%E5%91%A8/</guid><description><![CDATA[<p>由于明天要去团建，后天一大早就要赶火车回长沙。所以周报今天先完成。后面每周都会写下我对生活的思考。</p>
<h2 id="本周的生活概述-">本周的生活概述 ：</h2>
<p>周六，参加了公司组织的年度体检。今年我对去年发现的甲状腺结节问题尤为关注，特意增加了甲功三项B专项检查。体检过程中，医生还建议我增加两项指标检测：抗甲状腺球蛋白抗体(TG-Ab)和甲状腺过氧化物酶抗体(TPO-Ab)，用于诊断是否患有桥本甲状腺炎。当天下午，血液检查结果就可以通过小程序同步查看结果显示我的TG-Ab高达78.14(IU/ml 正常范围0-4.11), TPO-Ab高达28.6(IU/ml 正常范围0-5.63)。</p>
<p>这对我来说，就是暴击，无疑就已经宣判了我患桥本了。后面仔细想了想，我体检前一天晚上没怎么睡好，且前一阵子不是吃烧烤就是出去喝奶茶，加上从媳妇老家带过来的辣椒酱爱不释手，可能这两个指标飙升和自身的生活习惯有关。在阅读了和桥本相关的医学知识后，感觉我从此要和辣椒无缘了，我可是正宗的湖南人啊，没有辣椒我能活？媳妇还在一旁不停的讲风凉话。不过我媳妇，也就是讲讲，心理比谁都更加重视我的健康。才30岁的我，身体就已经开始下滑，这让我开始反思自己。在这之前，我从来认为吃饭不就是一项任务？随意吃一点就好。以为自己很年轻，有更多的事情比吃饭，睡觉更加重要。现在想想，我真的有点大错特错了，对于现在的我们来说，其实最重要的是照顾好身体，身体才是我们的本钱，没有本钱，怎么去实现自己的价值呢？</p>
<h2 id="成长与学习-">成长与学习 ：</h2>
<ul>
<li><del>阅读完成《真需求》梁宁</del></li>
<li>阅读《亲密关系》罗兰.米勒 20%</li>
<li>阅读《桥本甲状腺炎90天治疗方案》20%</li>
</ul>
<h2 id="健康与自我关爱-">健康与自我关爱 ：</h2>
<h3 id="圆环闭合情况">圆环闭合情况：</h3>
<p>
自从检查出桥本后，我基本每天早上半小时运动，中午半小时运动，晚饭后半小时运动，然后调整饮食，一个月后再去复查，看看指标有没有下降或好转的可能。</p>
<h2 id="下周的计划-">下周的计划 ：</h2>
<p>下周准备回长沙，搞开荒，然后软装进场。终于房子装修告一段落了。诶，从买房到现在已经月光了一整年。希望月光的时间赶紧过去，然后尽自己最大的能力存钱。</p>
<h2 id="乐趣与感恩-">乐趣与感恩 ：</h2>
<p>从看到诊断结果到现在整整一周了，她每天坚决执行清淡饮食，督促我早睡早起，每天早上出门锻炼30分钟。在这里我非常感谢我媳妇在背后对我的支持。</p>
]]></description></item><item><title>当下的事情</title><link>https://blog.ethan-ai.cn/%E5%BD%93%E4%B8%8B%E4%B8%93%E6%B3%A8/</link><pubDate>Thu, 14 Nov 2024 08:10:11 +0800</pubDate><author>作者</author><guid>https://blog.ethan-ai.cn/%E5%BD%93%E4%B8%8B%E4%B8%93%E6%B3%A8/</guid><description><![CDATA[<h1 id="当下">当下</h1>
<p>本页记录当下我需要专注的事情。更新于2024/12/02 于中国武汉</p>
<h2 id="生活">生活</h2>
<ul>
<li>日常工作：练习专注，寻找目标感
<ul>
<li>项目稳步推进</li>
<li>测试同学的挑衅淡定对待，工作而已</li>
</ul>
</li>
<li>业余生活：稳定作息，健康生活
<ul>
<li>坚持做饭</li>
<li>有节奏的作息，拒绝熬夜</li>
</ul>
</li>
<li>运动健身：提高基础代谢
<ul>
<li>开始跑步，每周至少两次</li>
<li>继续羽毛球运动</li>
</ul>
</li>
</ul>
<h2 id="学习">学习</h2>
<ul>
<li>读书：
<ul>
<li>阅读《build a large language model from scratch》 60%</li>
<li><del>阅读《真需求》梁宁</del></li>
<li>阅读《亲密关系》罗兰.米勒 80%</li>
<li>阅读《桥本甲状腺炎90天治疗方案》20%</li>
<li>阅读《learning-ebpf》5%</li>
</ul>
</li>
<li>技术：
<ul>
<li>学习深度包解析技术</li>
<li>学习TCP协议相关知识</li>
<li>学习rust相关知识</li>
</ul>
</li>
<li>写作：
<ul>
<li>提升写作方面的能力</li>
</ul>
</li>
</ul>
<h2 id="项目">项目</h2>
<ul>
<li>流量采集器</li>
</ul>
]]></description></item><item><title>认证订阅</title><link>https://blog.ethan-ai.cn/%E8%AE%A4%E8%AF%81follow%E8%AE%A2%E9%98%85%E6%BA%90/</link><pubDate>Thu, 24 Oct 2024 00:00:00 +0800</pubDate><author>作者</author><guid>https://blog.ethan-ai.cn/%E8%AE%A4%E8%AF%81follow%E8%AE%A2%E9%98%85%E6%BA%90/</guid><description>&lt;p>This message is used to verify that this feed (feedId:71916462721158158) belongs to me (userId:45764741539537920). Join me in enjoying the next generation information browser &lt;a href="https://follow.is" target="_blank" rel="noopener noreffer">https://follow.is&lt;/a>.&lt;/p></description></item><item><title>写在28岁的中点</title><link>https://blog.ethan-ai.cn/%E5%86%99%E5%9C%A828%E5%B2%81%E7%9A%84%E4%B8%AD%E7%82%B9/</link><pubDate>Sun, 08 Jan 2023 00:00:00 +0800</pubDate><author>作者</author><guid>https://blog.ethan-ai.cn/%E5%86%99%E5%9C%A828%E5%B2%81%E7%9A%84%E4%B8%AD%E7%82%B9/</guid><description><![CDATA[<p>Dear Ethan:</p>
<p>又到了充满期待的新的一年，过去的一年你过得还好吗？在往年的年终总结中，你会对即将到来的一年许下满满的期待。很明显，去年的你又是欠下满满债务的你。选择在你28岁的中点写一封信给自己，这更私人，但也更贴近你的内心。</p>
<h2 id="命途多舛何以不甘">命途多舛，何以不甘</h2>
<p>又一年的时间，你经历了不同的事情，遇到了不同的人，了解了不同的故事，现在轮到你说一说自己的故事了。也许都听过关于西西弗斯的故事，他的一生就是不断将巨石推到山顶，又不得不经受巨石滚落，再将石头推向山顶，这样一个荒诞的周而复始的故事。这也许，也是我们每一个人所需要经历的人生。</p>
<p>三月到五月的你，在小论文、毕业论文修改和实验中度过，那时的你，年轻气盛，因为一点小小的观点和老师争吵的面红耳赤。六月份经历了毕业的狂欢，阔别了昔日一起学习的良师益友，与好友约定毕业旅行，因囊中羞涩与疫情的封控而取消。急忙奔赴职场，结实新的朋友，重新投入到自我的升华之中。总的来说，去年的你，经历了人生中的两件大事：毕业和工作，再次完成学生到职场人身份的转变，其它的都是一地鸡毛。</p>
<p>这一年中失去的东西太多太多，任何一点细小的死亡与崩坏都会变得不可承受，这大概就是去年的一个缩影吧，巨石一次次的滚动，我们一次次的再上路。真的很想努力，但满满的无力感。这种无力感，年复一年，细细沉思，最早可追溯到2015年，那是我第一次深刻体会这种无力感。如今七年已过，你仍旧在与这种无力感继续搏斗着。</p>
<p>此前的每一个人生阶段&mdash;-初中，高中，大学，似乎总是被安排着走的，大的方向永远是一年比一年好。那份不甘于现实的热情，还能继续保持，也许正是因为不曾经历大的挫折。仔细回忆过往的人生，之前的你确实保持着点自我。那会儿呢，只需要考虑自己就已经足够了，家人永远是不断给予付出的那一方，所以那会儿做什么事都是那么天真吧！这份自我得益于你的少年意气，得益于家庭给你的支撑，也得益于时代的滚滚向前。但人生或命运从来就没有承诺过谁，总会往更好的方向发展。巨石总会滚落，而明天一早睁眼，我们依旧需要推着巨石往上。</p>
<h2 id="肩负起自己的责任">肩负起自己的责任</h2>
<p>去年的你，每一天都在慌慌张张中度过，连家人都没能好好陪伴，也没有很好的意识到，父母的年纪已经到了颐养天年的时刻，我们需要无时不刻的关注着，陪伴着他们。而你每一天都在焦虑中挣扎，却无法鼓起勇气，让现在的你有所改观，因为你此刻内心是害怕的，害怕试错的代价太大，害怕失败，害怕被人嘲笑。可是，正如上面所说，人生或命运从来就没有承诺过谁，总会往更好的方向发展，所以今年的你，一定要鼓起勇气做出一点改变啦！</p>
<p>我知道在过去的一年，你无数次打开B站，似乎想要寻找什么答案，可是刷了很久，焦虑一点没减少。事实一次次的告诉你，既然别人无法明确的告诉你，那你就要学会戴着镣铐和生活共舞，不是吗？毛姆在写《月亮与六便士》的时候，大概忘了在理想和现实中间还有责任。他没有告诉你站在路口，抬头是月亮，低头是捡硬币，责任在肩膀上压着，那你该往哪儿走。你唯一确定的是，你想负起这个责任。因为曾经家人的支持是你的底气，你今天，同样想成为家人的底气。</p>
<h2 id="所谓成长接受自我">所谓成长，接受自我</h2>
<p>直到现在我才真正的意识到，所谓的成长就是认知的不断升级。只有当你明白这个道理，这个世界才开始真正的展开在你的眼前，原来以前认为错误的事情，原来也可以是对的「之前和老师争论的面红耳赤」。你不再为某一个你不认同的点去争论，慢慢的学会去理解别人，尊重别人，倾听大家的声音，不再自我，这已经是你最大的成长了。到了这个年纪才谈成长，这也许是一件过于奢侈的事情了。有很多很多的人，已经过早的品尝了世间的滋味。但对于刚入社会的我来说，考验才刚刚开始。成长不是随着年龄的增长，被社会打磨成一样的世故和圆滑，而是在生命的成熟中，仍有一颗纯真的童心和一颗善良的爱心。你想得到月亮，即使如此的平凡，不能起飞，也要努力的走着，跑着，伸手去够，去摘。即使经历过种种不顺，还是会有好事发生，会有新的缘分，新的身份，新的挑战，我不认输，你也不要，好吗？</p>
<h2 id="寄语未来">寄语未来</h2>
<p>2023年，愿你在不平和焦虑的时候，能记起你的初心和梦想，然后大踏步的坚持走向明天！！！</p>
]]></description></item><item><title>读《程序员修炼之道》</title><link>https://blog.ethan-ai.cn/%E8%AF%BB%E7%A8%8B%E5%BA%8F%E5%91%98%E4%BF%AE%E7%82%BC%E4%B9%8B%E9%81%93/</link><pubDate>Mon, 02 Jan 2023 00:00:00 +0800</pubDate><author>作者</author><guid>https://blog.ethan-ai.cn/%E8%AF%BB%E7%A8%8B%E5%BA%8F%E5%91%98%E4%BF%AE%E7%82%BC%E4%B9%8B%E9%81%93/</guid><description><![CDATA[<h2 id="务实的哲学">务实的哲学</h2>
<ul>
<li>
<p>团队信任对于创造力和协作至关重要，关键时刻信任的破坏几乎无法修复</p>
</li>
<li>
<p>提供选择，别找借口&ndash; 小黄鸭编程</p>
</li>
<li>
<p>破窗理论&ndash; 不要因为一些危急的事情，造成附加伤害，尽可能控制软件的熵</p>
</li>
<li>
<p>人们都觉得，加入一个推进中的成功项目更容易一些（煮石头汤的故事）</p>
</li>
<li>
<p>永远审视项目，不要做温水青蛙，先养成仔细观察周围环境的习惯，然后再项目中这样做</p>
</li>
<li>
<p>知识和经验是你最重要的资产，但是它们是时效资产，学习新事物的能力是你最重要的战略资产。 知识组合：</p>
<ol>
<li>
<p>定期投资&ndash;安排一个固定的时间和地点学习</p>
<ul>
<li>每年学习一门新语言</li>
<li>每月读一本技术书</li>
<li>读非技术书</li>
<li>上课&ndash; 了解公司之外的人都在做什么</li>
<li>尝试不同的环境</li>
<li>与时俱进&ndash;关心最新技术的进展</li>
</ul>
<p>想法的交叉是很重要的 批判性思维&ndash;批判性思考独到的和听到的东西</p>
</li>
<li>
<p>多样化&ndash; 熟悉的技能越多越好</p>
</li>
<li>
<p>风险管理&ndash;不同技术在高风险高回报到低风险低回报区间均匀分布，不要把技术鸡蛋放在一个篮子里</p>
</li>
<li>
<p>低买高卖&ndash;在一项新兴技术流行之前就开始学习，不过这是押宝</p>
</li>
<li>
<p>重新评估调整&ndash;不断刷新自己的知识库</p>
</li>
</ol>
</li>
<li>
<p>批判性思维</p>
<ol>
<li>五次为什么</li>
<li>谁从中收益</li>
<li>有什么背景</li>
<li>什么时候在哪里工作可以工作起来</li>
<li>为什么是这个问题</li>
</ol>
</li>
<li>
<p>写一个大纲， 问自己：这是否用正确的方式表达了我想表达的东西，以及现在是表达这个东西的好时机吗？</p>
</li>
</ul>
<h2 id="务实的方法">务实的方法</h2>
<h3 id="etceasy-to-change">ETC（easy to change）</h3>
<p>核心知道思想</p>
<ul>
<li>DRY(Don&rsquo;t repeat yourself)</li>
<li>正交性 良好设计中，数据库相关代码应该和用户界面保持正交， 当系统的组件相互之间高度依赖时，就没有局部修理这回事。</li>
</ul>
]]></description></item></channel></rss>