1 场景定义与需求分析

在动手写代码前,需要明确系统要解决的核心问题。飞秒激光加工领域知识分散在学术论文、设备手册、实验记录和专利中,工程师常面临"查参数半天、读文献一天"的困境。

典型问题示例:

  • "如何在石英玻璃上加工高深宽比的微孔,同时减少热影响区?"

  • "针对不锈钢表面LIPSS结构,脉宽和通量的一般工艺窗口是多少?"

  • "双光子聚合加工中,如何避免结构坍塌?"

基于Langchain的特性,我们将在实现中重点关注多源文档加载、领域术语的分块策略、混合检索架构这三个关键点。

2 环境准备与依赖安装

2.1 基础环境配置

bash

# 创建专用虚拟环境
python -m venv fs_laser_rag
source fs_laser_rag/bin/activate  # Linux/Mac
# 或 fs_laser_rag\Scripts\activate  # Windows

# 安装核心依赖
pip install langchain langchain-community langchain-openai chromadb
pip install pypdf docx2txt unstructured  # 文档解析
pip install tiktoken  # 文本分块
pip install sentence-transformers  # 本地嵌入模型

2.2 飞秒加工领域特定准备

收集初始知识库文档,建议从以下三类开始:

  • 综述论文PDF:如飞秒激光与材料相互作用的综述

  • 设备操作手册:激光器参数说明、加工系统指南

  • 实验记录表格:工艺参数与结果的对应关系

3 文档加载与预处理(Langchain Loader实战)

飞秒激光加工文档格式多样,Langchain提供了丰富的文档加载器。

3.1 多格式文档加载器实现

python

import os
from langchain_community.document_loaders import (
    PyPDFLoader, 
    TextLoader, 
    Docx2txtLoader,
    CSVLoader,
    UnstructuredMarkdownLoader
)

def load_fs_laser_documents(data_dir: str):
    """加载飞秒激光加工领域的多格式文档"""
    documents = []
    
    for root, dirs, files in os.walk(data_dir):
        for file in files:
            file_path = os.path.join(root, file)
            
            if file.endswith('.pdf'):
                # 学术论文PDF加载
                loader = PyPDFLoader(file_path)
                docs = loader.load()
                # 为每个文档块添加元数据
                for doc in docs:
                    doc.metadata["source_type"] = "academic_paper"
                    doc.metadata["file_name"] = file
                documents.extend(docs)
                
            elif file.endswith('.txt') or file.endswith('.md'):
                # 实验记录或说明文档
                loader = TextLoader(file_path, encoding='utf-8')
                docs = loader.load()
                for doc in docs:
                    doc.metadata["source_type"] = "lab_note"
                    doc.metadata["file_name"] = file
                documents.extend(docs)
                
            elif file.endswith('.docx'):
                loader = Docx2txtLoader(file_path)
                docs = loader.load()
                for doc in docs:
                    doc.metadata["source_type"] = "manual"
                    doc.metadata["file_name"] = file
                documents.extend(docs)
                
            elif file.endswith('.csv'):
                # 工艺参数表格
                loader = CSVLoader(file_path)
                docs = loader.load()
                for doc in docs:
                    doc.metadata["source_type"] = "parameter_table"
                    doc.metadata["file_name"] = file
                documents.extend(docs)
    
    print(f"加载完成,共 {len(documents)} 个文档块")
    return documents

# 使用示例
docs = load_fs_laser_documents("./fs_laser_knowledge_base")

关键决策点:飞秒激光加工文档常包含图表和公式,纯文本加载会丢失信息。对于PDF中的图表,后续可考虑多模态模型提取,初期建议优先选择文本丰富的综述文章。

3.2 领域特定的文档清洗

python

import re

def clean_fs_laser_document(doc):
    """针对飞秒激光加工领域的文本清洗"""
    text = doc.page_content
    
    # 保留专业术语的格式(如FWHM、LIPSS等)
    # 但去除不必要的换行和特殊字符
    
    # 处理常见的PDF提取问题
    text = re.sub(r'-\n', '', text)  # 连字符换行连接
    text = re.sub(r'\n+', ' ', text)  # 多个换行替换为空格
    
    # 提取关键参数(正则示例)
    # 寻找"脉宽: 35 fs"、"波长: 800 nm"这类模式
    param_pattern = r'([脉波长功频])[长宽率]\s*[:\:]\s*(\d+\.?\d*)\s*(fs|nm|W|kHz|MHz)'
    params = re.findall(param_pattern, text)
    
    if params:
        doc.metadata["extracted_params"] = params
    
    doc.page_content = text
    return doc

# 应用清洗
cleaned_docs = [clean_fs_laser_document(doc) for doc in docs]

4 文档分块策略(Chunking)

飞秒激光加工文档中,技术描述和参数往往紧密相关。分块策略需要保持语义单元的完整性

4.1 智能分块实现

python

from langchain.text_splitter import RecursiveCharacterTextSplitter

def create_fs_laser_chunks(documents):
    """针对飞秒激光文档的智能分块"""
    
    # 基础分块器配置
    text_splitter = RecursiveCharacterTextSplitter(
        chunk_size=1000,  # 可根据文档长度调整
        chunk_overlap=200,  # 重叠保证上下文连续性
        separators=["\n\n", "\n", "。", ";", ",", " "],  # 优先保持段落完整
        length_function=len,
    )
    
    chunks = text_splitter.split_documents(documents)
    
    # 为每个块添加领域标签
    for i, chunk in enumerate(chunks):
        # 根据内容自动判断主题
        content = chunk.page_content.lower()
        if any(word in content for word in ["烧蚀", "ablation", "微孔", "hole"]):
            chunk.metadata["process_type"] = "ablation"
        elif any(word in content for word in ["聚合", "polymerization", "双光子"]):
            chunk.metadata["process_type"] = "polymerization"
        elif any(word in content for word in ["光栅", "LIPSS", "条纹"]):
            chunk.metadata["process_type"] = "structuring"
        
        chunk.metadata["chunk_id"] = i
    
    print(f"分块完成,共 {len(chunks)} 个知识块")
    return chunks

chunks = create_fs_laser_chunks(cleaned_docs)

5 向量化与存储(Embedding + VectorStore)

5.1 嵌入模型选择

飞秒激光加工领域的术语具有专业性,建议使用领域适配性较好的嵌入模型

python

from langchain_openai import OpenAIEmbeddings
from langchain_community.embeddings import HuggingFaceEmbeddings

# 方案A:使用OpenAI(需要API密钥)
# embeddings = OpenAIEmbeddings(model="text-embedding-ada-002")

# 方案B:使用本地开源模型(推荐,保护数据隐私)
embeddings = HuggingFaceEmbeddings(
    model_name="BAAI/bge-large-zh-v1.5",  # 中文场景推荐
    # 或 "BAAI/bge-large-en-v1.5"  # 英文场景
    model_kwargs={'device': 'cpu'},
    encode_kwargs={'normalize_embeddings': True}
)

5.2 向量数据库构建

python

from langchain_community.vectorstores import Chroma
import chromadb

# 创建持久化向量数据库
persist_directory = "./fs_laser_chroma_db"

# 初始化Chroma
vectordb = Chroma.from_documents(
    documents=chunks,
    embedding=embeddings,
    persist_directory=persist_directory,
    collection_name="fs_laser_knowledge",
    collection_metadata={
        "hnsw:space": "cosine",  # 余弦相似度
        "description": "飞秒激光加工知识库"
    }
)

# 持久化保存
vectordb.persist()
print(f"向量数据库已保存至 {persist_directory}")

6 检索增强生成(RAG Chain构建)

6.1 基础检索器配置

python

# 从持久化数据库加载
vectordb = Chroma(
    persist_directory=persist_directory,
    embedding_function=embeddings,
    collection_name="fs_laser_knowledge"
)

# 创建检索器
retriever = vectordb.as_retriever(
    search_type="similarity",  # 可选 "similarity" 或 "mmr"
    search_kwargs={
        "k": 5,  # 返回前5个最相关文档
        "fetch_k": 20,  # MMR时预取的文档数
    }
)

6.2 混合检索增强

飞秒激光加工问题常涉及精确参数,需要结合关键词检索。

python

from langchain.retrievers import EnsembleRetriever
from langchain_community.retrievers import BM25Retriever

# 创建BM25检索器(关键词匹配)
bm25_retriever = BM25Retriever.from_documents(
    chunks,
    k=3
)

# 创建混合检索器
ensemble_retriever = EnsembleRetriever(
    retrievers=[bm25_retriever, retriever],
    weights=[0.3, 0.7]  # 关键词检索权重30%,语义检索70%
)

6.3 Prompt模板设计

针对飞秒激光加工领域,设计专门的提示模板。

python

from langchain.prompts import ChatPromptTemplate

# 飞秒激光加工专用提示模板
fs_laser_prompt = ChatPromptTemplate.from_messages([
    ("system", """你是一个飞秒激光加工领域的专家助手。请基于提供的知识片段,准确回答用户关于飞秒激光加工的问题。

回答要求:
1. 如果涉及工艺参数(功率、脉宽、波长等),请用清晰的格式呈现
2. 区分理论解释和实验数据,分别标注来源
3. 如果不确定或知识片段中没有相关信息,请明确说明
4. 对于工艺建议,注明参数范围仅供参考,实际需根据设备调试

知识片段:
{context}
"""),
    ("human", "{question}")
])

6.4 完整RAG Chain构建

python

from langchain_openai import ChatOpenAI
from langchain.chains import RetrievalQA
from langchain_core.runnables import RunnablePassthrough
from langchain_core.output_parsers import StrOutputParser

# 初始化LLM(可使用本地模型或API)
llm = ChatOpenAI(
    model="gpt-4",  # 或使用本地部署的模型
    temperature=0.1,  # 低温度保证准确性
    max_tokens=1000
)

# 构建RAG链
def format_docs(docs):
    return "\n\n".join([f"[来源: {doc.metadata.get('source_type', '未知')}, 文件: {doc.metadata.get('file_name', '未知')}]\n{doc.page_content}" for doc in docs])

rag_chain = (
    {"context": ensemble_retriever | format_docs, "question": RunnablePassthrough()}
    | fs_laser_prompt
    | llm
    | StrOutputParser()
)

7 查询测试与效果评估

7.1 基础测试

python

# 测试查询
test_questions = [
    "飞秒激光加工石英玻璃微孔的最佳参数范围是多少?",
    "如何减少不锈钢表面LIPSS结构的粗糙度?",
    "双光子聚合加工中,结构坍塌的原因和解决方法"
]

for question in test_questions:
    print(f"\n问题: {question}")
    print("-" * 50)
    answer = rag_chain.invoke(question)
    print(f"回答: {answer}")
    print("=" * 70)

7.2 检索质量诊断

python

def inspect_retrieval(question, retriever, k=5):
    """检查检索到的文档质量"""
    docs = retriever.get_relevant_documents(question)
    print(f"\n问题: {question}")
    print(f"检索到 {len(docs)} 个相关文档:\n")
    
    for i, doc in enumerate(docs):
        print(f"--- 文档 {i+1} ---")
        print(f"来源: {doc.metadata.get('source_type', '未知')}")
        print(f"内容预览: {doc.page_content[:200]}...")
        print()

# 诊断示例
inspect_retrieval("飞秒激光加工石英玻璃微孔的工艺参数", ensemble_retriever)

8 进阶优化策略

8.1 多查询检索(Multi-Query)

python

from langchain.retrievers.multi_query import MultiQueryRetriever

# 使用LLM生成多个相关查询,提高召回率
multi_query_retriever = MultiQueryRetriever.from_llm(
    retriever=ensemble_retriever,
    llm=llm,
    prompt=ChatPromptTemplate.from_template(
        """你是一个飞秒激光加工专家。请针对用户的原始问题,生成5个不同角度但相关的查询,以提高检索覆盖率。
原始问题: {question}
生成5个不同角度的查询(每行一个):"""
    )
)

8.2 上下文压缩(Contextual Compression)

python

from langchain.retrievers import ContextualCompressionRetriever
from langchain.retrievers.document_compressors import LLMChainExtractor

# 使用LLM提取最相关的内容片段,减少上下文长度
compressor = LLMChainExtractor.from_llm(llm)
compression_retriever = ContextualCompressionRetriever(
    base_compressor=compressor,
    base_retriever=ensemble_retriever
)

8.3 飞秒激光加工知识图谱增强

python

# 伪代码示例:构建简单的实体关系网络
fs_laser_kg = {
    "material": ["石英玻璃", "不锈钢", "硅", "钙钛矿"],
    "process": ["烧蚀", "聚合", "改性", "LIPSS"],
    "parameter": ["脉宽", "波长", "功率", "重复频率"],
    "effect": ["热影响区", "粗糙度", "深宽比", "衍射效率"]
}

# 可在检索时根据实体类型调整权重
def kg_enhanced_retrieval(question, retriever, kg):
    # 识别问题中的实体类型
    # 根据实体类型调整检索参数
    pass

9 部署与服务化

9.1 简单Web服务(FastAPI)

python

from fastapi import FastAPI
from pydantic import BaseModel
import uvicorn

app = FastAPI(title="飞秒激光加工RAG系统")

class Query(BaseModel):
    question: str
    history: list = []

class Response(BaseModel):
    answer: str
    sources: list

@app.post("/ask", response_model=Response)
async def ask_fs_laser(query: Query):
    answer = rag_chain.invoke(query.question)
    
    # 获取来源文档
    docs = ensemble_retriever.get_relevant_documents(query.question)
    sources = [
        {
            "content": doc.page_content[:200],
            "source_type": doc.metadata.get("source_type", ""),
            "file_name": doc.metadata.get("file_name", "")
        }
        for doc in docs[:3]
    ]
    
    return Response(answer=answer, sources=sources)

if __name__ == "__main__":
    uvicorn.run(app, host="0.0.0.0", port=8000)

9.2 交互式命令行工具

python

# cli.py
def main():
    print("=" * 60)
    print("飞秒激光加工RAG助手 - 输入问题,Q退出")
    print("=" * 60)
    
    while True:
        question = input("\n请输入问题: ")
        if question.upper() == 'Q':
            break
        
        print("\n正在检索...")
        answer = rag_chain.invoke(question)
        print(f"\n回答: {answer}\n")
        print("-" * 60)

if __name__ == "__main__":
    main()

10 系统迭代路线图

基于类似项目的经验,飞秒激光加工RAG系统的迭代可按以下阶段推进:

阶段 目标 关键任务
Phase 1 MVP验证 加载20-30篇核心文献,测试基础问答效果
Phase 2 知识库扩充 加入设备手册、专利、实验记录,覆盖更多加工场景
Phase 3 检索优化 实现混合检索、多查询、上下文压缩,提高准确率
Phase 4 多模态增强 集成图表理解能力,支持从SEM图像检索相似案例
Phase 5 用户闭环 部署Web服务,收集真实用户反馈,持续优化

总结

通过以上步骤,我们实现了:

  1. 文档加载:支持飞秒激光加工领域的PDF、Word、TXT、CSV等多种格式

  2. 智能分块:针对技术文档特点,保持段落和参数的完整性

  3. 向量存储:使用领域适配的嵌入模型,构建持久化向量数据库

  4. 混合检索:结合语义检索和关键词检索,提高召回质量

  5. RAG链:设计领域专用Prompt,生成准确、可追溯的回答

  6. 服务化:提供API接口和命令行工具,方便实际使用

这套系统可以作为飞秒激光加工研究的智能知识助手,帮助工程师快速获取工艺参数、理解加工机理、借鉴已有案例,大幅提升研发效率。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐