基于 RAG 的智能写作系统实现

基于 RAG 的智能写作系统实现

我是deepMan,毕业于985高校,拥有丰富的大厂工作经验,现任独立开发者公司负责人。
长期专注于技术创新,精通人工智能和区块链技术,具备丰富的项目开发和管理经验,
擅长从0到1构建高质量技术解决方案,致力于推动技术在实际场景中的应用落地。
获取源码与答疑解惑,添加小助手微信,进行业交流群,一起抱团,开拓新赛道,获取新财富机会。

关注更多有意思的内容访问历史文章:答疑与更多源码
在这里插入图片描述
在这里插入图片描述

系统架构

  1. 文档处理模块 (DocumentProcessor)

    • 支持 PDF 和 Markdown 文件加载
    • 使用 RecursiveCharacterTextSplitter 进行文本分割
    • 针对中文优化的分隔符设置
  2. RAG 系统核心 (RAGSystem)

    • 使用 shaw/dmeta-embedding-zh 进行文本向量化
    • 使用 qwq 模型进行文本生成
    • 基于 Chroma 的向量数据库存储
  3. 内容生成器 (ContentGenerator)

    • 写作风格分析
    • 主题提取
    • 文章生成
    • 自动保存功能

工作流程

1. 文档学习阶段

2. 写作风格分析

  • 分析样本文档的句式结构
  • 提取段落组织方式
  • 识别用词特点
  • 总结行文逻辑

3. 文章生成过程

  1. 大纲生成

    outline_prompt = """基于标题"{title}",请生成一个详细的文章大纲,
    要求:
    1. 符合文档库中的专业领域知识
    2. 包含3-5个主要部分
    3. 每个部分2-3个子要点
    """
    
  2. 内容生成

    article_prompt = """请基于以下大纲和已学习的文档风格,撰写一篇完整的文章:
    标题:{title}
    大纲:{outline}
    
    要求:
    1. 符合原有文档的专业性和准确性
    2. 保持一致的写作风格
    3. 字数不少于{min_words}字
    4. 注意段落之间的连贯性
    """
    
  3. 文章保存格式

    # 文章标题
    
    ## 大纲
    [自动生成的大纲内容]
    
    ## 正文
    [生成的文章内容]
    
    ---
    生成时间:[时间戳]
    

使用示例


import os
from typing import List, Dict
import json
from datetime import datetime
import random
from pathlib import Path
from langchain_community.document_loaders import DirectoryLoader, PyPDFLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.vectorstores import Chroma
from langchain.chains import ConversationalRetrievalChain
from langchain.memory import ConversationBufferMemory
from langchain.docstore.document import Document
from langchain_ollama import OllamaEmbeddings, OllamaLLM
import time
from tqdm import tqdm
import asyncio
from concurrent.futures import ThreadPoolExecutor

class DocumentProcessor:
    def __init__(self):
        # 初始化文本分割器
        self.text_splitter = RecursiveCharacterTextSplitter(
            chunk_size=500,
            chunk_overlap=100,
            length_function=len,
            separators=["\n\n", "\n", "。", "!", "?", ".", " ", ""]
        )
        
    def load_documents(self, directory_path: str) -> List[Document]:
        """加载指定目录下的 PDF 和 Markdown 文件"""
        documents = []
        
        # 加载 PDF 文件
        pdf_loader = DirectoryLoader(
            directory_path, 
            glob="**/*.pdf",
            loader_cls=PyPDFLoader  # type: ignore
        )
        
        # 加载 Markdown 文件
        markdown_loader = DirectoryLoader(
            directory_path, 
            glob="**/*.md"
        )
        
        try:
            pdf_docs = pdf_loader.load()
            documents.extend(pdf_docs)
            print(f"已加载 {len(pdf_docs)} 个 PDF 文件")
        except Exception as e:
            print(f"加载 PDF 文件时出错: {str(e)}")
            
        try:
            md_docs = markdown_loader.load()
            documents.extend(md_docs)
            print(f"已加载 {len(md_docs)} 个 Markdown 文件")
        except Exception as e:
            print(f"加载 Markdown 文件时出错: {str(e)}")
            
        return documents

    def split_documents(self, documents: List[Document]) -> List[Document]:
        """将文档分割成小块"""
        return self.text_splitter.split_documents(documents)

class RAGSystem:
    def __init__(self, persist_directory: str = "./chroma_db"):
        self.persist_directory = persist_directory
        # 使用新的 OllamaEmbeddings
        self.embeddings = OllamaEmbeddings(
            model="shaw/dmeta-embedding-zh",
            base_url="http://localhost:11434"
        )
        # 使用新的 OllamaLLM 替代 Ollama
        self.llm = OllamaLLM(
            model="llama3:latest",
            base_url="http://localhost:11434"
        )
        
    def create_vectorstore(self, documents: List[Document]) -> Chroma:
        """创建向量数据库"""
        return Chroma.from_documents(
            documents=documents,
            embedding=self.embeddings,
            persist_directory=self.persist_directory
        )
        
    def create_qa_chain(self, vectorstore: Chroma) -> ConversationalRetrievalChain:
        """创建问答链"""
        memory = ConversationBufferMemory(
            memory_key="chat_history",
            return_messages=True,
            output_key="answer"
        )
        
        return ConversationalRetrievalChain.from_llm(
            llm=self.llm,
            retriever=vectorstore.as_retriever(
                search_kwargs={"k": 3}  # 检索最相关的3个文档片段
            ),
            memory=memory,
            return_source_documents=True  # 返回源文档信息
        )

class ContentGenerator:
    def __init__(self, qa_chain, vectorstore):
        self.qa_chain = qa_chain
        self.vectorstore = vectorstore
        self.style_patterns = []
        self.learned_topics = set()
        
    def analyze_writing_style(self, documents: List[Document]):
        """分析文档的写作风格"""
        style_prompt = """分析这段文本的写作风格特点,包括:
        1. 常用句式
        2. 段落结构
        3. 用词特点
        4. 行文逻辑
        请简要总结。
        
        文本内容:{text}
        """
        
        sample_size = min(5, len(documents))
        print(f"\n开始分析写作风格,将分析 {sample_size} 个文档样本...")
        
        for i, doc in enumerate(documents[:sample_size], 1):
            print(f"\n分析第 {i}/{sample_size} 个文档:")
            print(f"文档来源: {doc.metadata.get('source', '未知来源')}")
            
            # 添加进度条和超时处理
            with tqdm(total=100, desc="分析进度", ncols=100) as pbar:
                try:
                    # 创建定时更新进度的函数
                    def update_progress():
                        progress = 0
                        while progress < 95:
                            time.sleep(0.5)
                            step = min(5, 95 - progress)
                            progress += step
                            pbar.update(step)
                            
                    # 在后台线程中更新进度
                    with ThreadPoolExecutor() as executor:
                        future = executor.submit(update_progress)
                        
                        # 设置超时时间为60秒
                        response = self.qa_chain.invoke({
                            "question": style_prompt.format(text=doc.page_content)
                        })
                        
                        # 完成进度条
                        pbar.update(100 - pbar.n)
                        
                    print("✓ 分析完成!")
                    print(f"分析结果: {response['answer'][:100]}...")
                    self.style_patterns.append(response["answer"])
                    
                except Exception as e:
                    print(f"\n❌ 分析失败: {str(e)}")
                    continue
        
        successful_patterns = len(self.style_patterns)
        print(f"\n写作风格分析完成!成功分析 {successful_patterns}/{sample_size} 个文档。")
        if successful_patterns > 0:
            print("已获取写作风格模式。")
        else:
            print("警告:未能成功分析任何文档的写作风格。")
    
    def extract_topics(self, documents: List[Document]):
        """提取文档中的主题"""
        topic_prompt = "请从以下文本中提取主要主题和关键概念:{text}"
        
        for doc in documents:
            response = self.qa_chain.invoke({
                "question": topic_prompt.format(text=doc.page_content)
            })
            # 解析响应中的主题
            topics = self._parse_topics(response["answer"])
            self.learned_topics.update(topics)
    
    def _parse_topics(self, text: str) -> set:
        """从文本中解析主题"""
        # 这里可以使用更复杂的主题提取算法
        words = text.replace(',', ',').replace('。', '.').split()
        return set(word for word in words if len(word) > 1)
    
    def generate_article(self, title: str, min_words: int = 800) -> Dict:
        """根据标题生成文章"""
        # 构建文章大纲
        outline_prompt = f"""基于标题"{title}",请生成一个详细的文章大纲,
        要求:
        1. 符合文档库中的专业领域知识
        2. 包含3-5个主要部分
        3. 每个部分2-3个子要点
        """
        
        outline_response = self.qa_chain.invoke({"question": outline_prompt})
        outline = outline_response["answer"]
        
        # 根据大纲生成文章
        article_prompt = f"""请基于以下大纲和已学习的文档风格,撰写一篇完整的文章:
        标题:{title}
        大纲:{outline}
        
        要求:
        1. 符合原有文档的专业性和准确性
        2. 保持一致的写作风格
        3. 字数不少于{min_words}字
        4. 注意段落之间的连贯性
        """
        
        article_response = self.qa_chain.invoke({"question": article_prompt})
        
        return {
            "title": title,
            "outline": outline,
            "content": article_response["answer"],
            "timestamp": datetime.now().isoformat(),
            "reference_style": random.choice(self.style_patterns)
        }
    
    def save_article(self, article: Dict, output_dir: str = "generated_articles"):
        """保存生成的文章"""
        Path(output_dir).mkdir(exist_ok=True)
        
        filename = f"{article['title']}_{datetime.now().strftime('%Y%m%d_%H%M%S')}.md"
        filepath = os.path.join(output_dir, filename)
        
        with open(filepath, 'w', encoding='utf-8') as f:
            f.write(f"# {article['title']}\n\n")
            f.write(f"## 大纲\n\n{article['outline']}\n\n")
            f.write(f"## 正文\n\n{article['content']}\n\n")
            f.write(f"\n---\n生成时间:{article['timestamp']}")

class AutoLearningSystem:
    def __init__(self, doc_processor: DocumentProcessor, rag_system: RAGSystem):
        self.doc_processor = doc_processor
        self.rag_system = rag_system
        self.content_generator = None
        
    def learn_documents(self, directory_path: str):
        """自动学习文档内容"""
        print("开始加载文档...")
        documents = self.doc_processor.load_documents(directory_path)
        
        if not documents:
            raise ValueError("未找到任何文档")
            
        print("分割文档...")
        splits = self.doc_processor.split_documents(documents)
        
        print("创建向量数据库...")
        vectorstore = self.rag_system.create_vectorstore(splits)
        
        print("创建问答链...")
        qa_chain = self.rag_system.create_qa_chain(vectorstore)
        
        print("初始化内容生成器...")
        self.content_generator = ContentGenerator(qa_chain, vectorstore)
        
        print("分析写作风格...")
        self.content_generator.analyze_writing_style(splits)
        
        print("提取主题概念...")
        self.content_generator.extract_topics(splits)
        
        print("学习完成!")
        return self.content_generator
    
    def generate_articles(self, titles: List[str], output_dir: str = "generated_articles"):
        """批量生成文章"""
        if not self.content_generator:
            raise ValueError("请先调用 learn_documents 进行学习")
            
        generated_articles = []
        for title in titles:
            print(f"正在生成文章:{title}")
            article = self.content_generator.generate_article(title)
            self.content_generator.save_article(article, output_dir)
            generated_articles.append(article)
            
        return generated_articles

def main():
    # 初始化系统
    doc_processor = DocumentProcessor()
    rag_system = RAGSystem()
    auto_learning = AutoLearningSystem(doc_processor, rag_system)
    
    # 指定文档目录
    directory_path = "./doc"
    
    try:
        # 自动学习文档
        content_generator = auto_learning.learn_documents(directory_path)
        
        # 示例:生成一些文章
        titles = [
            "人工智能在医疗领域的应用前景",
            "数字化转型对企业的影响",
            "可持续发展与绿色能源的未来"
        ]
        
        generated_articles = auto_learning.generate_articles(titles)
        
        print(f"\n成功生成 {len(generated_articles)} 篇文章!")
        print("文章已保存到 generated_articles 目录")
        
    except Exception as e:
        print(f"处理过程中出错: {str(e)}")

if __name__ == "__main__":
    main()


<think>好的,我现在要解决用户在IAR编译器中遇到的错误:“incomplete type is not allowed”。首先,我需要理解这个错误的含义和常见原因。根据我的知识库,这个错误常发生在代码中使用了未完全定义的类型,比如结构体或联合体声明了但未定义,或者前向声明了某个类型但没有提供完整的定义。 首先,我应该检查用户代码中是否有结构体或联合体的声明但没有定义的情况。比如,如果有一个结构体声明为`struct MyStruct;`,但没有后续的完整定义,那么在需要该结构体大小或成员的地方,编译器就会报这个错误。这时候,需要用户补全结构体的定义,包括成员变量。 其次,检查是否在头文件中只声明了类型而没有包含定义。例如,如果在头文件中使用前向声明,但在使用该类型的源文件中没有包含实际的定义头文件,这也会导致错误。此时,需要确保在使用类型的地方包含了正确的头文件,或者将前向声明替换为完整的定义。 另外,还要考虑typedef的问题。如果用户使用了typedef创建别名,但原始类型未完全定义,同样会引发错误。例如,`typedef struct MyStruct MyType;`之后,如果尝试使用MyType但MyStruct尚未定义,就会出错。需要确保在使用typedef之前,原始类型已经被完整定义。 此外,数组声明中的类型不完整也是一个常见原因。比如声明数组时,如果元素类型未完全定义,编译器无法确定数组的大小,导致错误。需要检查数组声明中的类型是否已经完整定义。 可能还有其他情况,比如在C和C++混合编程时,如果没有正确使用extern "C",或者在头文件中遗漏了必要的包含,也可能导致类型不完整。需要用户检查这些方面。 接下来,我需要将这些可能的原因整理成清晰的解决步骤,并给出具体的代码示例,帮助用户理解如何修改。同时,根据用户提供的引用内容,虽然引用[1]和引用[2]是关于其他错误的,但用户可能希望看到类似的结构,即分点说明解决方案,并引用相关资源。 最后,生成相关问题,帮助用户进一步了解可能相关的编译错误,比如类型未定义、前向声明问题等。这样用户可以有更全面的认识,预防其他类似错误的发生。</think>### IAR编译器错误'incomplete type is not allowed'解决方案 该错误表示代码中使用了**未完全定义的类型**,编译器无法确定其内存布局或成员信息。以下是具体原因及解决方法: #### 1. 结构体/联合体未定义完整 若声明了结构体但未定义成员,在实例化时会报错: ```c struct MyStruct; // 前向声明不完整 struct MyStruct var; // 错误:使用未完成类型 ``` **解决方案**:补全类型定义 ```c struct MyStruct { int id; char name[20]; }; ``` #### 2. 头文件包含缺失 当跨文件使用类型时,需确保定义可见: ```c // file.h struct Data; // 前向声明 // file.c struct Data { // 实际定义 int value; }; ``` **解决方案**:在使用该类型的文件中包含定义头文件 ```c #include "file.c" // 包含实际定义 ``` #### 3. typedef别名问题 使用typedef时原始类型必须完整: ```c typedef struct Node NodeT; // 前向声明 NodeT* ptr; // 允许指针声明 NodeT instance; // 错误:不完整类型 ``` **解决方案**:先完成类型定义再typedef ```c struct Node { int data; struct Node* next; }; typedef struct Node NodeT; ``` #### 4. 数组声明不完整 数组元素类型必须完全定义: ```c struct Element; struct Element arr[10]; // 错误:元素类型未定义 ``` **解决方案**: ```c struct Element { int type; float value; }; struct Element arr[10]; // 合法 ``` #### 调试建议 1. 在IAR工程中搜索错误行号定位问题代码 2. 使用Go to Definition功能追踪类型定义 3. 检查所有头文件包含链 4. 确认没有循环依赖的头文件 编译器需要知道类型的完整信息才能: - 计算sizeof大小 - 分配内存空间 - 访问成员变量 - 进行类型对齐 [^1]: 类似类型转换错误可参考浮点转整型的类型适配问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值