{"id":15,"date":"2026-02-10T10:00:00","date_gmt":"2026-02-10T02:00:00","guid":{"rendered":"http:\/\/www.gmrea.com\/?p=15"},"modified":"2026-02-10T10:00:00","modified_gmt":"2026-02-10T02:00:00","slug":"langchain-03-qa-system","status":"publish","type":"post","link":"https:\/\/gmrea.com\/?p=15","title":{"rendered":"LangChain \u5b9e\u6218\uff1a\u6784\u5efa\u95ee\u7b54\u7cfb\u7edf"},"content":{"rendered":"<p>\u672c\u6559\u7a0b\u5c06\u5e26\u4f60\u4ece\u96f6\u5f00\u59cb\uff0c\u4f7f\u7528 LangChain \u6784\u5efa\u4e00\u4e2a\u5b8c\u6574\u7684\u95ee\u7b54\u7cfb\u7edf\uff08Q&amp;A\uff09\u3002<\/p>\n<p>## \u9879\u76ee\u6982\u8ff0<br \/>\n\u6211\u4eec\u8981\u6784\u5efa\u4e00\u4e2a\u667a\u80fd\u95ee\u7b54\u7cfb\u7edf\uff0c\u5b83\u80fd\u591f\uff1a\u63a5\u53d7\u7528\u6237\u95ee\u9898\u3001\u4ece\u77e5\u8bc6\u5e93\u4e2d\u68c0\u7d22\u76f8\u5173\u6587\u6863\u3001\u4f7f\u7528 LLM \u751f\u6210\u51c6\u786e\u7684\u56de\u7b54\u3001\u652f\u6301\u591a\u79cd\u6587\u6863\u683c\u5f0f\u3002<\/p>\n<p>## \u51c6\u5907\u5de5\u4f5c<br \/>\n&#8220;`bash<br \/>\npip install langchain langchain-openai langchain-community langchain-chroma python-dotenv<br \/>\n&#8220;`<\/p>\n<p>## \u7b2c\u4e00\u6b65\uff1a\u521b\u5efa\u6587\u6863\u52a0\u8f7d\u5668<br \/>\n&#8220;`python<br \/>\nfrom langchain_core.documents import Document<br \/>\nfrom pathlib import Path<\/p>\n<p>def load_text_files(directory: str) -&gt; list[Document]:<br \/>\n    documents = []<br \/>\n    for file_path in Path(directory).rglob(&#8220;*.txt&#8221;):<br \/>\n        with open(file_path, &#8216;r&#8217;, encoding=&#8217;utf-8&#8242;) as f:<br \/>\n            content = f.read()<br \/>\n        doc = Document(<br \/>\n            page_content=content,<br \/>\n            metadata={&#8220;source&#8221;: str(file_path)}<br \/>\n        )<br \/>\n        documents.append(doc)<br \/>\n    return documents<br \/>\n&#8220;`<\/p>\n<p>## \u7b2c\u4e8c\u6b65\uff1a\u6587\u6863\u5206\u5757<br \/>\n&#8220;`python<br \/>\nfrom langchain_text_splitters import RecursiveCharacterTextSplitter<\/p>\n<p>def split_documents(documents, chunk_size=1000, chunk_overlap=200):<br \/>\n    text_splitter = RecursiveCharacterTextSplitter(<br \/>\n        chunk_size=chunk_size,<br \/>\n        chunk_overlap=chunk_overlap,<br \/>\n        separators=[&#8220;nn&#8221;, &#8220;n&#8221;, &#8220;\u3002&#8221;, &#8220;\uff01&#8221;, &#8220;\uff1f&#8221;, &#8220;\uff0c&#8221;, &#8220;\u3001&#8221;, &#8221; &#8220;]<br \/>\n    )<br \/>\n    chunks = text_splitter.split_documents(documents)<br \/>\n    return chunks<br \/>\n&#8220;`<\/p>\n<p>## \u7b2c\u4e09\u6b65\uff1a\u521b\u5efa\u5411\u91cf\u5b58\u50a8<br \/>\n&#8220;`python<br \/>\nfrom langchain_openai import OpenAIEmbeddings<br \/>\nfrom langchain_chroma import Chroma<\/p>\n<p>def create_vectorstore(documents, persist_directory=&#8221;.\/chroma_db&#8221;):<br \/>\n    embeddings = OpenAIEmbeddings(model=&#8221;text-embedding-3-small&#8221;)<br \/>\n    vectorstore = Chroma.from_documents(<br \/>\n        documents=documents,<br \/>\n        embedding=embeddings,<br \/>\n        persist_directory=persist_directory<br \/>\n    )<br \/>\n    return vectorstore<br \/>\n&#8220;`<\/p>\n<p>## \u7b2c\u56db\u6b65\uff1a\u521b\u5efa\u95ee\u7b54\u94fe<br \/>\n&#8220;`python<br \/>\nfrom langchain.chains import create_retrieval_chain<br \/>\nfrom langchain.chains.combine_documents import create_stuff_documents_chain<br \/>\nfrom langchain_openai import ChatOpenAI<br \/>\nfrom langchain_core.prompts import ChatPromptTemplate<\/p>\n<p>def create_qa_chain(retriever, model_name=&#8221;gpt-4o-mini&#8221;):<br \/>\n    llm = ChatOpenAI(model=model_name, temperature=0)<\/p>\n<p>    template = &#8220;&#8221;&#8221;<br \/>\n\u4f60\u662f\u4e00\u4e2a\u4e13\u4e1a\u7684\u95ee\u7b54\u52a9\u624b\u3002\u8bf7\u6839\u636e\u4ee5\u4e0b\u4e0a\u4e0b\u6587\u4fe1\u606f\u56de\u7b54\u7528\u6237\u7684\u95ee\u9898\u3002<\/p>\n<p>\u4e0a\u4e0b\u6587\u4fe1\u606f:<br \/>\n{context}<\/p>\n<p>\u7528\u6237\u95ee\u9898: {question}<\/p>\n<p>\u8981\u6c42:<br \/>\n1. \u53ea\u4f7f\u7528\u4e0a\u4e0b\u6587\u4e2d\u7684\u4fe1\u606f\u56de\u7b54<br \/>\n2. \u5982\u679c\u4e0a\u4e0b\u6587\u4e2d\u6ca1\u6709\u7b54\u6848\uff0c\u8bf7\u660e\u786e\u8bf4\u660e&#8221;\u6211\u65e0\u6cd5\u4ece\u63d0\u4f9b\u7684\u4fe1\u606f\u4e2d\u627e\u5230\u7b54\u6848&#8221;<br \/>\n3. \u56de\u7b54\u8981\u51c6\u786e\u3001\u7b80\u6d01\u3001\u6709\u6761\u7406<br \/>\n4. \u4f7f\u7528\u4e2d\u6587\u56de\u7b54<\/p>\n<p>\u56de\u7b54:<br \/>\n&#8220;&#8221;&#8221;<br \/>\n    prompt = ChatPromptTemplate.from_template(template)<br \/>\n    combine_docs_chain = create_stuff_documents_chain(llm=llm, prompt=prompt)<br \/>\n    retrieval_chain = create_retrieval_chain(retriever, combine_docs_chain)<br \/>\n    return retrieval_chain<br \/>\n&#8220;`<\/p>\n<p>## \u7b2c\u4e94\u6b65\uff1a\u6574\u5408\u6240\u6709\u7ec4\u4ef6<br \/>\n&#8220;`python<br \/>\nimport os<br \/>\nfrom dotenv import load_dotenv<br \/>\nload_dotenv()<\/p>\n<p>DATA_DIR = &#8220;.\/data\/knowledge_base&#8221;<br \/>\nVECTOR_DB_DIR = &#8220;.\/chroma_db&#8221;<br \/>\nMODEL_NAME = &#8220;gpt-4o-mini&#8221;<\/p>\n<p># \u52a0\u8f7d\u6587\u6863<br \/>\ndocuments = load_text_files(DATA_DIR)<br \/>\nchunks = split_documents(documents)<\/p>\n<p># \u521b\u5efa\u5411\u91cf\u5b58\u50a8<br \/>\nvectorstore = create_vectorstore(chunks, VECTOR_DB_DIR)<br \/>\nretriever = vectorstore.as_retriever(search_kwargs={&#8220;k&#8221;: 3})<\/p>\n<p># \u521b\u5efa\u95ee\u7b54\u94fe<br \/>\nqa_chain = create_qa_chain(retriever, MODEL_NAME)<\/p>\n<p># \u4ea4\u4e92\u5f0f\u95ee\u7b54<br \/>\nwhile True:<br \/>\n    question = input(&#8220;\u4f60: &#8220;).strip()<br \/>\n    if question.lower() in [&#8216;quit&#8217;, &#8216;exit&#8217;, &#8216;\u9000\u51fa&#8217;]:<br \/>\n        break<br \/>\n    result = qa_chain.invoke({&#8220;query&#8221;: question})<br \/>\n    print(f&#8221;\u52a9\u624b: {result[&#8216;answer&#8217;]}&#8221;)<br \/>\n&#8220;`<\/p>\n","protected":false},"excerpt":{"rendered":"<p>\u672c\u6559\u7a0b\u5c06\u5e26\u4f60\u4ece\u96f6\u5f00\u59cb\uff0c\u4f7f\u7528 LangChain \u6784\u5efa\u4e00\u4e2a\u5b8c\u6574\u7684\u95ee\u7b54\u7cfb\u7edf\uff08Q&amp;A\uff09\u3002 ## \u9879\u76ee\u6982\u8ff0 \u6211 [&hellip;]<\/p>\n","protected":false},"author":0,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"zakra_page_container_layout":"customizer","zakra_page_sidebar_layout":"customizer","zakra_remove_content_margin":false,"zakra_sidebar":"customizer","zakra_transparent_header":"customizer","zakra_logo":0,"zakra_main_header_style":"default","zakra_menu_item_color":"","zakra_menu_item_hover_color":"","zakra_menu_item_active_color":"","zakra_menu_active_style":"","zakra_page_header":true,"footnotes":""},"categories":[7],"tags":[],"class_list":["post-15","post","type-post","status-publish","format-standard","hentry","category-langchain"],"_links":{"self":[{"href":"https:\/\/gmrea.com\/index.php?rest_route=\/wp\/v2\/posts\/15","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/gmrea.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/gmrea.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"replies":[{"embeddable":true,"href":"https:\/\/gmrea.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=15"}],"version-history":[{"count":0,"href":"https:\/\/gmrea.com\/index.php?rest_route=\/wp\/v2\/posts\/15\/revisions"}],"wp:attachment":[{"href":"https:\/\/gmrea.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=15"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/gmrea.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=15"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/gmrea.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=15"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}