MoreRSS

site iconEthan修改

从事⽹络安全与深度学习结合以及网络协议分析相关开发⼯作, 专注⾼速报⽂处理及 DPI 技术。
请复制 RSS 到你的阅读器,或快速订阅到 :

Inoreader Feedly Follow Feedbin Local Reader

Ethan的 RSS 预览

RAG系列-语义分块RAG(Semantic Chunking RAG)

2025-06-18 00:00:00

02. 语义分块RAG(Semantic Chunking RAG)

方法简介

语义分块RAG通过计算句子间的语义相似度来智能分块,而不是简单的固定长度分块。它使用百分位数、标准差或四分位距等方法找到语义断点,将文本分割成语义连贯的块,提升检索精度。

核心代码

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
import fitz
import os
import numpy as np
import json
from openai import OpenAI

def extract_text_from_pdf(pdf_path):
    """
    Extracts text from a PDF file.

    Args:
    pdf_path (str): Path to the PDF file.

    Returns:
    str: Extracted text from the PDF.
    """
    # Open the PDF file
    mypdf = fitz.open(pdf_path)
    all_text = ""  # Initialize an empty string to store the extracted text

    # Iterate through each page in the PDF
    for page in mypdf:
        # Extract text from the current page and add spacing
        all_text += page.get_text("text") + " "

    # Return the extracted text, stripped of leading/trailing whitespace
    return all_text.strip()

# Initialize the OpenAI client with the base URL and API key
client = OpenAI(
    base_url="https://api.studio.nebius.com/v1/",
    api_key=os.getenv("OPENAI_API_KEY")  # Retrieve the API key from environment variables
)

def get_embedding(text, model="BAAI/bge-en-icl"):
    """
    Creates an embedding for the given text using OpenAI.

    Args:
    text (str): Input text.
    model (str): Embedding model name.

    Returns:
    np.ndarray: The embedding vector.
    """
    response = client.embeddings.create(model=model, input=text)
    return np.array(response.data[0].embedding)

def cosine_similarity(vec1, vec2):
    """
    Computes cosine similarity between two vectors.

    Args:
    vec1 (np.ndarray): First vector.
    vec2 (np.ndarray): Second vector.

    Returns:
    float: Cosine similarity.
    """
    return np.dot(vec1, vec2) / (np.linalg.norm(vec1) * np.linalg.norm(vec2))

def compute_breakpoints(similarities, method="percentile", threshold=90):
    """
    Computes chunking breakpoints based on similarity drops.

    Args:
    similarities (List[float]): List of similarity scores between sentences.
    method (str): 'percentile', 'standard_deviation', or 'interquartile'.
    threshold (float): Threshold value (percentile for 'percentile', std devs for 'standard_deviation').

    Returns:
    List[int]: Indices where chunk splits should occur.
    """
    # Determine the threshold value based on the selected method
    if method == "percentile":
        # Calculate the Xth percentile of the similarity scores
        threshold_value = np.percentile(similarities, threshold)
    elif method == "standard_deviation":
        # Calculate the mean and standard deviation of the similarity scores
        mean = np.mean(similarities)
        std_dev = np.std(similarities)
        # Set the threshold value to mean minus X standard deviations
        threshold_value = mean - (threshold * std_dev)
    elif method == "interquartile":
        # Calculate the first and third quartiles (Q1 and Q3)
        q1, q3 = np.percentile(similarities, [25, 75])
        # Set the threshold value using the IQR rule for outliers
        threshold_value = q1 - 1.5 * (q3 - q1)
    else:
        # Raise an error if an invalid method is provided
        raise ValueError("Invalid method. Choose 'percentile', 'standard_deviation', or 'interquartile'.")

    # Identify indices where similarity drops below the threshold value
    return [i for i, sim in enumerate(similarities) if sim < threshold_value]

def split_into_chunks(sentences, breakpoints):
    """
    Splits sentences into semantic chunks.

    Args:
    sentences (List[str]): List of sentences.
    breakpoints (List[int]): Indices where chunking should occur.

    Returns:
    List[str]: List of text chunks.
    """
    chunks = []  # Initialize an empty list to store the chunks
    start = 0  # Initialize the start index

    # Iterate through each breakpoint to create chunks
    for bp in breakpoints:
        # Append the chunk of sentences from start to the current breakpoint
        chunks.append(". ".join(sentences[start:bp + 1]) + ".")
        start = bp + 1  # Update the start index to the next sentence after the breakpoint

    # Append the remaining sentences as the last chunk
    chunks.append(". ".join(sentences[start:]))
    return chunks  # Return the list of chunks

def create_embeddings(text_chunks):
    """
    Creates embeddings for each text chunk.

    Args:
    text_chunks (List[str]): List of text chunks.

    Returns:
    List[np.ndarray]: List of embedding vectors.
    """
    # Generate embeddings for each text chunk using the get_embedding function
    return [get_embedding(chunk) for chunk in text_chunks]

def semantic_search(query, text_chunks, chunk_embeddings, k=5):
    """
    Finds the most relevant text chunks for a query.

    Args:
    query (str): Search query.
    text_chunks (List[str]): List of text chunks.
    chunk_embeddings (List[np.ndarray]): List of chunk embeddings.
    k (int): Number of top results to return.

    Returns:
    List[str]: Top-k relevant chunks.
    """
    # Generate an embedding for the query
    query_embedding = get_embedding(query)

    # Calculate cosine similarity between the query embedding and each chunk embedding
    similarities = [cosine_similarity(query_embedding, emb) for emb in chunk_embeddings]

    # Get the indices of the top-k most similar chunks
    top_indices = np.argsort(similarities)[-k:][::-1]

    # Return the top-k most relevant text chunks
    return [text_chunks[i] for i in top_indices]

def generate_response(system_prompt, user_message, model="meta-llama/Llama-3.2-3B-Instruct"):
    """
    Generates a response from the AI model based on the system prompt and user message.

    Args:
    system_prompt (str): The system prompt to guide the AI's behavior.
    user_message (str): The user's message or query.
    model (str): The model to be used for generating the response. Default is "meta-llama/Llama-2-7B-chat-hf".

    Returns:
    dict: The response from the AI model.
    """
    response = client.chat.completions.create(
        model=model,
        temperature=0,
        messages=[
            {"role": "system", "content": system_prompt},
            {"role": "user", "content": user_message}
        ]
    )
    return response

# 完整调用流程
def semantic_chunking_rag_pipeline(pdf_path, query):
    # 1. 提取PDF文本
    extracted_text = extract_text_from_pdf(pdf_path)

    # 2. 按句子分割
    sentences = extracted_text.split(". ")

    # 3. 生成句子嵌入
    embeddings = [get_embedding(sentence) for sentence in sentences]

    # 4. 计算句子间相似度
    similarities = [cosine_similarity(embeddings[i], embeddings[i + 1]) for i in range(len(embeddings) - 1)]

    # 5. 计算断点(使用百分位数方法)
    breakpoints = compute_breakpoints(similarities, method="percentile", threshold=90)

    # 6. 分割成语义块
    text_chunks = split_into_chunks(sentences, breakpoints)

    # 7. 创建块嵌入
    chunk_embeddings = create_embeddings(text_chunks)

    # 8. 语义搜索
    top_chunks = semantic_search(query, text_chunks, chunk_embeddings, k=2)

    # 9. 生成回答
    system_prompt = "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: 'I do not have enough information to answer that.'"
    user_prompt = "\n".join([f"Context {i + 1}:\n{chunk}\n=====================================\n" for i, chunk in enumerate(top_chunks)])
    user_prompt = f"{user_prompt}\nQuestion: {query}"

    ai_response = generate_response(system_prompt, user_prompt)
    return ai_response.choices[0].message.content

代码讲解

  • 句子分割:按句号分割文本成句子
  • 嵌入生成:为每个句子生成向量表示
  • 相似度计算:计算相邻句子的余弦相似度
  • 断点检测:使用百分位数方法找到语义断点
  • 语义分块:根据断点将句子组合成语义块
  • 检索生成:基于语义块进行检索和答案生成

主要特点

  • 基于语义相似度的智能分块
  • 支持多种断点检测方法(百分位数、标准差、四分位距)
  • 保持语义连贯性
  • 比固定长度分块更精准

使用场景

  • 长文档处理
  • 需要保持语义完整性的场景
  • 复杂问答系统
  • 学术论文、技术文档等结构化文本

RAG系列-基础RAG(Simple RAG)

2025-06-18 00:00:00

01. 基础RAG(Simple RAG)

方法简介

基础RAG(Retrieval-Augmented Generation)是最简单的检索增强生成方法。它通过向量化检索获取与用户查询最相关的文档片段,并将这些片段作为上下文输入给大语言模型进行答案生成。

核心代码

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
import fitz
import os
import numpy as np
import json
from openai import OpenAI

def extract_text_from_pdf(pdf_path):
    """
    Extracts text from a PDF file and prints the first `num_chars` characters.

    Args:
    pdf_path (str): Path to the PDF file.

    Returns:
    str: Extracted text from the PDF.
    """
    # Open the PDF file
    mypdf = fitz.open(pdf_path)
    all_text = ""  # Initialize an empty string to store the extracted text

    # Iterate through each page in the PDF
    for page_num in range(mypdf.page_count):
        page = mypdf[page_num]  # Get the page
        text = page.get_text("text")  # Extract text from the page
        all_text += text  # Append the extracted text to the all_text string

    return all_text  # Return the extracted text

def chunk_text(text, n, overlap):
    """
    Chunks the given text into segments of n characters with overlap.

    Args:
    text (str): The text to be chunked.
    n (int): The number of characters in each chunk.
    overlap (int): The number of overlapping characters between chunks.

    Returns:
    List[str]: A list of text chunks.
    """
    chunks = []  # Initialize an empty list to store the chunks

    # Loop through the text with a step size of (n - overlap)
    for i in range(0, len(text), n - overlap):
        # Append a chunk of text from index i to i + n to the chunks list
        chunks.append(text[i:i + n])

    return chunks  # Return the list of text chunks

# Initialize the OpenAI client with the base URL and API key
client = OpenAI(
    base_url="https://api.studio.nebius.com/v1/",
    api_key=os.getenv("OPENAI_API_KEY")  # Retrieve the API key from environment variables
)

def create_embeddings(text, model="BAAI/bge-en-icl"):
    """
    Creates embeddings for the given text using the specified OpenAI model.

    Args:
    text (str): The input text for which embeddings are to be created.
    model (str): The model to be used for creating embeddings. Default is "BAAI/bge-en-icl".

    Returns:
    dict: The response from the OpenAI API containing the embeddings.
    """
    # Create embeddings for the input text using the specified model
    response = client.embeddings.create(
        model=model,
        input=text
    )

    return response  # Return the response containing the embeddings

def cosine_similarity(vec1, vec2):
    """
    Calculates the cosine similarity between two vectors.

    Args:
    vec1 (np.ndarray): The first vector.
    vec2 (np.ndarray): The second vector.

    Returns:
    float: The cosine similarity between the two vectors.
    """
    # Compute the dot product of the two vectors and divide by the product of their norms
    return np.dot(vec1, vec2) / (np.linalg.norm(vec1) * np.linalg.norm(vec2))

def semantic_search(query, text_chunks, embeddings, k=5):
    """
    Performs semantic search on the text chunks using the given query and embeddings.

    Args:
    query (str): The query for the semantic search.
    text_chunks (List[str]): A list of text chunks to search through.
    embeddings (List[dict]): A list of embeddings for the text chunks.
    k (int): The number of top relevant text chunks to return. Default is 5.

    Returns:
    List[str]: A list of the top k most relevant text chunks based on the query.
    """
    # Create an embedding for the query
    query_embedding = create_embeddings(query).data[0].embedding
    similarity_scores = []  # Initialize a list to store similarity scores

    # Calculate similarity scores between the query embedding and each text chunk embedding
    for i, chunk_embedding in enumerate(embeddings):
        similarity_score = cosine_similarity(np.array(query_embedding), np.array(chunk_embedding.embedding))
        similarity_scores.append((i, similarity_score))  # Append the index and similarity score

    # Sort the similarity scores in descending order
    similarity_scores.sort(key=lambda x: x[1], reverse=True)
    # Get the indices of the top k most similar text chunks
    top_indices = [index for index, _ in similarity_scores[:k]]
    # Return the top k most relevant text chunks
    return [text_chunks[index] for index in top_indices]

def generate_response(system_prompt, user_message, model="meta-llama/Llama-3.2-3B-Instruct"):
    """
    Generates a response from the AI model based on the system prompt and user message.

    Args:
    system_prompt (str): The system prompt to guide the AI's behavior.
    user_message (str): The user's message or query.
    model (str): The model to be used for generating the response. Default is "meta-llama/Llama-2-7B-chat-hf".

    Returns:
    dict: The response from the AI model.
    """
    response = client.chat.completions.create(
        model=model,
        temperature=0,
        messages=[
            {"role": "system", "content": system_prompt},
            {"role": "user", "content": user_message}
        ]
    )
    return response

# 完整调用流程
def simple_rag_pipeline(pdf_path, query):
    # 1. 提取PDF文本
    extracted_text = extract_text_from_pdf(pdf_path)

    # 2. 分块处理
    text_chunks = chunk_text(extracted_text, 1000, 200)

    # 3. 创建嵌入
    response = create_embeddings(text_chunks)

    # 4. 语义搜索
    top_chunks = semantic_search(query, text_chunks, response.data, k=2)

    # 5. 生成回答
    system_prompt = "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: 'I do not have enough information to answer that.'"
    user_prompt = "\n".join([f"Context {i + 1}:\n{chunk}\n=====================================\n" for i, chunk in enumerate(top_chunks)])
    user_prompt = f"{user_prompt}\nQuestion: {query}"

    ai_response = generate_response(system_prompt, user_prompt)
    return ai_response.choices[0].message.content

代码讲解

  • 文档处理:使用PyMuPDF提取PDF文本,按字符数分块
  • 嵌入生成:使用BAAI/bge-en-icl模型生成文本嵌入
  • 语义搜索:计算查询与文档块的余弦相似度,返回最相关的k个片段
  • 答案生成:将检索到的上下文与用户问题输入LLM生成答案

主要特点

  • 实现简单,易于理解和扩展
  • 使用余弦相似度进行语义检索
  • 支持PDF文档处理
  • 可配置的检索数量k

使用场景

  • FAQ自动问答
  • 小型企业知识库
  • 结构化文档检索增强
  • 基础文档问答系统

2025年展望

2025-01-01 00:00:00

2024年回顾

1月-2月:安家落户

终于完成了人生中的一件大事 - 买房。拿到房产证的那一刻,我和妻子都感到无比欣喜。这个新家不仅是一个住所,更是我们对未来生活的美好期待。

3月-4月:平稳前行

这段时间主要是日常工作和还贷。虽然每个月能存下的钱不多,但生活依然充满欢乐。我们学会了在有限的预算中寻找生活的乐趣。

5月-6月:职场动荡

公司经历了裁员风波,虽然我幸免于难,但这次事件让我对公司的未来产生了疑虑。这段时间充满了迷茫和不确定性,尝试了很多事情但进展不大。

7月-10月:装修新家

新房进入装修阶段,虽然经济压力较大,但每周回长沙监工的过程充满了期待和喜悦。看着新家一点点成型,所有的辛苦都值得。

11月-12月:健康与AI探索

体检发现患有桥本甲状腺炎,这让我开始更加关注身体健康。同时,AI技术的快速发展让我产生了强烈的危机感。经过深入思考和实践,我决定拥抱AI而不是恐惧它。

这段时间,我深入探索了多种AI工具:

  • Cursor
  • Windsurf(开通了Pro版)
  • Cline
  • Aider
  • Zed AI

通过实践,我发现每种工具都有其独特优势,于是开始尝试多种工具结合使用。12月中旬,我开始利用AI接一些外包项目,主要目的有两个:

  1. 缓解经济压力
  2. 深入探索AI能力,提升工作效率

2025年展望

在新的一年里,我为自己设定了以下目标:

  1. AI SOP优化:总结出一套适合自己的AI使用流程和标准操作程序
  2. 产品开发:借助AI工具,完成第一个产品的MVP(最小可行产品)
  3. 全栈开发:开始探索全栈式开发,提升技术广度
  4. AI辅助学习:建立高效的AI辅助学习体系,加速知识获取
  5. 事业基础:为未来的事业发展打下坚实基础

2025年将是充满挑战和机遇的一年。我相信,通过合理利用AI工具,持续学习和自我提升,我能够实现这些目标,为未来创造更多可能性。

“未来属于那些相信梦想之美的人。” - 埃莉诺·罗斯福

windsurf编码体验

2024-12-06 17:32:26

最近,我有幸体验了windsurf编辑器的AI辅助编程功能。这款编辑器的编程体验令人印象深刻,在某些方面甚至超越了我使用过的其他主流编辑器。然而,经过一天的深入使用后,我发现它仍存在一些需要改进的地方。

最初,我期望AI能够完全接管代码编写工作,或者至少大幅减少我在低级编码任务上的时间投入,让我能够专注于更高层次的架构设计。然而,现实与期望存在差距。在持续使用过程中,我注意到AI的解决问题的能力会随着会话时间的延长而逐渐下降。

经过分析,我认为这可能与windsurf的架构设计有关。该编辑器的AI辅助功能基于agent模式开发,在使用过程中我频繁遇到响应错误,且这种错误会随着使用时间的增加而变得更加频繁。更令人困扰的是,有时AI虽然能够正确回答问题,却无法实际修改代码来解决问题。我推测这可能是windsurf中存在的一个bug,导致AI无法持续执行代码修改任务。

值得注意的是,AI辅助编程的核心能力并非来自其"智能",而是源于其对代码的深度分析和理解能力。AI之所以能够快速解决问题,是因为它能够高效地分析代码之间的关联,理解代码的逻辑结构和生命周期。然而,随着会话的持续,这种分析和理解能力似乎会逐渐衰减,这可能是由于软件性能优化不足导致的。

要实现真正优雅的AI辅助编程,还需要在多个方面进行优化和改进。虽然短期内可能无法完全解决这些问题,但不可否认的是,AI辅助编程仍然是一个强大的工具。它能够帮助我们快速学习和掌握新的编程语言,显著提升学习效率和工作效能。相信随着技术的不断进步,这些问题终将得到解决,为开发者带来更优质的编程体验。

读付鹏和高善文对当前经济评论

2024-12-03 18:07:13

今天读了付鹏先生在HSBC内部演讲的文稿,后面相继听了高善文先生的演讲。在阅读了高善文和付鹏关于中国经济形势的深刻分析后,我获得了对当前和未来经济趋势的更全面理解。

通过深入阅读高善文的分析,我深刻认识到经济转型和周期性压力是塑造中国经济未来的关键力量。经济转型引发的结构性变化是深远而持久的,它要求我们不断适应新的发展模式,比如从劳动密集型向技术密集型转变,从投资驱动向消费驱动转变。与此同时,周期性压力则在短期内对经济产生显著影响,如需求波动、市场信心变化等,这些都可能对我们的职业和财务状况产生直接或间接的影响。

这种理解使我意识到在不同的经济周期阶段,需要采取不同的应对策略。目前,我们的职业生涯将长期处于这个经济周期的尾声阶段,这意味着整体经济环境、就业环境以及收入增长潜力都不能与经济高速发展时期同日而语。为了适应这些变化,我们需要认真评估自己所在行业在经济转型中的位置,以及未来可能的发展趋势。如果行业前景黯淡,可能需要考虑转行或提升技能以适应新兴行业的需求。在经济增速放缓的背景下,我们也需要更加谨慎地管理个人财务,包括减少不必要的债务、增加储蓄和投资于相对稳定的资产。然后调整消费习惯,避免过度消费,尤其是在经济不确定性较高时期,理性消费变得更加重要。也是时候考虑要开启副业,增加职业以外的收入,以应对可能的经济波动。希望能够更好地应对经济转型和周期性压力带来的挑战,同时也为未来可能出现的新机遇做好准备。

作为普通人,我们需要建立更为全面和深入的经济理解,以便在不断变化的经济环境中做出明智的决策。这两篇文章为我未来的财务规划和职业发展提供了宝贵的指导。

2024年第46周, 患上桥本了

2024-11-14 21:20:46

由于明天要去团建,后天一大早就要赶火车回长沙。所以周报今天先完成。后面每周都会写下我对生活的思考。

本周的生活概述 :

周六,参加了公司组织的年度体检。今年我对去年发现的甲状腺结节问题尤为关注,特意增加了甲功三项B专项检查。体检过程中,医生还建议我增加两项指标检测:抗甲状腺球蛋白抗体(TG-Ab)和甲状腺过氧化物酶抗体(TPO-Ab),用于诊断是否患有桥本甲状腺炎。当天下午,血液检查结果就可以通过小程序同步查看结果显示我的TG-Ab高达78.14(IU/ml 正常范围0-4.11), TPO-Ab高达28.6(IU/ml 正常范围0-5.63)。

这对我来说,就是暴击,无疑就已经宣判了我患桥本了。后面仔细想了想,我体检前一天晚上没怎么睡好,且前一阵子不是吃烧烤就是出去喝奶茶,加上从媳妇老家带过来的辣椒酱爱不释手,可能这两个指标飙升和自身的生活习惯有关。在阅读了和桥本相关的医学知识后,感觉我从此要和辣椒无缘了,我可是正宗的湖南人啊,没有辣椒我能活?媳妇还在一旁不停的讲风凉话。不过我媳妇,也就是讲讲,心理比谁都更加重视我的健康。才30岁的我,身体就已经开始下滑,这让我开始反思自己。在这之前,我从来认为吃饭不就是一项任务?随意吃一点就好。以为自己很年轻,有更多的事情比吃饭,睡觉更加重要。现在想想,我真的有点大错特错了,对于现在的我们来说,其实最重要的是照顾好身体,身体才是我们的本钱,没有本钱,怎么去实现自己的价值呢?

成长与学习 :

  • 阅读完成《真需求》梁宁
  • 阅读《亲密关系》罗兰.米勒 20%
  • 阅读《桥本甲状腺炎90天治疗方案》20%

健康与自我关爱 :

圆环闭合情况:

自从检查出桥本后,我基本每天早上半小时运动,中午半小时运动,晚饭后半小时运动,然后调整饮食,一个月后再去复查,看看指标有没有下降或好转的可能。

下周的计划 :

下周准备回长沙,搞开荒,然后软装进场。终于房子装修告一段落了。诶,从买房到现在已经月光了一整年。希望月光的时间赶紧过去,然后尽自己最大的能力存钱。

乐趣与感恩 :

从看到诊断结果到现在整整一周了,她每天坚决执行清淡饮食,督促我早睡早起,每天早上出门锻炼30分钟。在这里我非常感谢我媳妇在背后对我的支持。