基于langchain的法律助手工作流的搭建

        该工作流有四个llm组成,包括三个worker以及一个planner。planner用于识别用户输入,将其划分为具体任务并调用相应的worker。worker则根据输入进行工作,三个worker分别用于法条翻译,法条查询以及案例分析。

        其中planner、lawtrans、lasearch使用的都是gpt4,embedding模型使用的是openai的

text-embedding-ada-002。
        caseana(案例分析)部分使用的是本地的qwen3-0.6B模型,。

        所以记得在本地配置下环境变量OPENAI_API_KEY以及下载qwen3-0.6B模型到本地。

        当然也可以根据自己的情况选择合适的模型。

一、worker搭建

1.法条翻译(lawTrans)

        该部分仅有一个llm组成,因为大部分大模型在语言翻译方面已经非常成熟了,所以无需过多加工。

from langchain_openai import ChatOpenAI
from langchain.prompts import PromptTemplate
from langchain.chains import LLMChain
from langchain.tools import Tool

# 初始化模型
llm = ChatOpenAI(model="gpt-4", temperature=0)

# 构建提示模板
translation_prompt = PromptTemplate.from_template(
    "请将以下法律条款翻译成{target_lang}:\n\n{text}\n\n只输出翻译结果"
)

# 构建 Chain
translation_chain = translation_prompt|llm

def _translate_law(text: str, target_lang: str = "英文") -> str:
    response = translation_chain.run(text=text, target_lang=target_lang)
    return response.strip()

# 包装为 LangChain Tool 对象
law_trans = Tool.from_function(
    func=_translate_law,
    name="LawTranslationTool",
    description="提供法律条文的中英文互译服务"
)

2.法条查询(lawSearch)

        该部分使用rag技术,创建了一个民法典知识库。使用了一个markdown格式的民法典文件,将其存储在向量数据库,当用于需要查找民法典具体内容是,llm会自动从该知识库中检索。

民法典下载链接

 向量数据库创建:

# scripts/build_vectorstore.py

from langchain_community.document_loaders import UnstructuredMarkdownLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_openai import OpenAIEmbeddings
from langchain_community.vectorstores import FAISS

# 加载 Markdown 文件
loader = UnstructuredMarkdownLoader("中华人民共和国民法典.md")
docs = loader.load()

# 切分文本
text_splitter = RecursiveCharacterTextSplitter(chunk_size=512, chunk_overlap=64)
split_docs = text_splitter.split_documents(docs)

# 构建向量数据库
embeddings = OpenAIEmbeddings(model="text-embedding-ada-002")
vectorstore = FAISS.from_documents(split_docs, embeddings)

# 保存到本地目录
vectorstore.save_local("vectorstore")
print("✅ 向量数据库已保存至 vectorstore/index.faiss 和 index.pkl")

lawsearch代码:

# tools/law_search_tool_rag.py


import os
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
from langchain_community.vectorstores import FAISS
from langchain.chains import RetrievalQA
from langchain.tools import Tool

# 向量数据库路径
VECTORSTORE_PATH = "D:/daima/law_agent/vectorstore"

# 检查 .faiss 和 .pkl 是否都存在
index_faiss = VECTORSTORE_PATH + "/index.faiss"
index_pkl = VECTORSTORE_PATH + "/index.pkl"

# 判断是否两个文件都存在
if os.path.exists(index_faiss) and os.path.exists(index_pkl):
    print(" 正在加载本地向量数据库...")
    embeddings = OpenAIEmbeddings(model="text-embedding-ada-002")
    vectorstore = FAISS.load_local(VECTORSTORE_PATH, embeddings, allow_dangerous_deserialization=True)
else:
    missing_files = []
    if not os.path.exists(index_faiss):
        missing_files.append(index_faiss)
    if not os.path.exists(index_pkl):
        missing_files.append(index_pkl)
    raise FileNotFoundError(f"以下文件缺失:{', '.join(missing_files)}。请先运行 build_vectorstore.py 构建数据库。")

# 创建 QA 检索链
llm = ChatOpenAI(model="gpt-4", temperature=0)
qa_chain = RetrievalQA.from_chain_type(
    llm=llm,
    chain_type="stuff",
    retriever=vectorstore.as_retriever(search_kwargs={"k": 3}),
    return_source_documents=True
)

def _search_law(query: str) -> str:
    """
    使用 RAG 技术在民法典中检索相关内容。
    :param query: 用户输入的问题或关键词
    """
    result = qa_chain.invoke({"query": query})
    answer = result.get("result", "未找到相关信息。")
    sources = "\n".join([f"来源片段:{doc.page_content}" for doc in result.get("source_documents", [])])
    return f"{answer}\n\n参考内容:\n{sources}"
# 包装为 LangChain Tool 对象
law_search_tool_rag = Tool.from_function(
    func=_search_law,
    name="LawSearchToolRAG",
    description="通过本地向量数据库搜索民法典内容及相关法律条文"
)

3.案例分析(caseana)

        该部分使用本地的qwen3-0.6B模型。我是从modescope平台下载的,具体下载方式可以在该网站学习。

        这个不分可以用对应的数据集对该模型进行了微调,不过微调出的模型我没发布,所以只用了原模型

通义千问3-0.6B模型

        

# agent/caseana.py

from langchain.prompts import PromptTemplate
from langchain.tools import Tool
from langchain_huggingface import HuggingFacePipeline
from transformers import AutoTokenizer, AutoModelForCausalLM, pipeline
from langchain_core.runnables import RunnableSequence, RunnablePassthrough
import torch

# 加载本地模型和分词器
model_path = "D:\daima\law_agent\Qwen3-0.6B"

# 初始化本地模型
tokenizer = AutoTokenizer.from_pretrained(model_path, trust_remote_code=True)
model = AutoModelForCausalLM.from_pretrained(
    model_path,
    trust_remote_code=True,
    torch_dtype=torch.float16,  # 使用半精度减少内存占用
    device_map="auto"  # 自动分配到可用设备
)

# 创建 HuggingFace 管道
hf_pipeline = pipeline(
    "text-generation",
    model=model,
    tokenizer=tokenizer,
    max_new_tokens=500,
    temperature=0.5,
    top_p=0.9,
    do_sample=True,
    pad_token_id=tokenizer.eos_token_id
)

# 创建 LangChain 兼容的 LLM (使用新的导入)
llm = HuggingFacePipeline(pipeline=hf_pipeline)

# 构建提示模板
ana_prompt = PromptTemplate.from_template(
    "请分析以下法律案例,并提取关键信息:\n{case_text}\n\n"
    "输出格式为:\n"
    "主体:xxx\n"
    "行为:xxx\n"
    "适用法律:xxx\n"
    "结论:xxx"
)


# 格式化输出函数
def format_output(response: str) -> str:
    """提取并格式化链的输出"""
    response = response.strip()

    # 确保格式正确
    if not response.startswith("主体:"):
        # 尝试提取关键部分
        for keyword in ["主体:", "行为:", "适用法律:", "结论:"]:
            pos = response.find(keyword)
            if pos != -1:
                response = response[pos:]
                break

    return response


# 使用 RunnableSequence 替代 LLMChain
analysis_chain = (
        RunnablePassthrough()  # 传递输入
        | {"case_text": RunnablePassthrough()}  # 创建字典结构
        | ana_prompt  # 应用提示模板
        | llm  # 调用模型
        | format_output  # 格式化输出
)

# 将函数包装成 Tool 对象
case_ana_tool = Tool.from_function(
    func=lambda inputs: analysis_chain.invoke(inputs),
    name="case_analysis",
    description="用于对法律案例进行分析,提取主体、行为、适用法律及结论"
)

# 测试函数
if __name__ == "__main__":
    test_case = """
    2023年5月,张某未经许可擅自使用李某的专利技术生产产品并销售获利。李某发现后向法院提起诉讼,
    要求张某停止侵权并赔偿损失50万元。法院经审理认为张某构成专利侵权,判决赔偿李某30万元。
    """

    print("测试案例:")
    print(test_case)

    result = case_ana_tool.run(test_case)
    print("\n案例分析结果:")
    print(result)

二、worker集成

        通过前面的代码可能有人发现,我们将每个worker都包装成了tool。我们就是将worker以工具的形式提供给planner的。

from agent.lawsearch import law_search_tool_rag
from agent.lawTrans import law_trans
from agent.caseana import case_ana_tool

tools = [
    law_trans,
    law_search_tool_rag,
    case_ana_tool
]

 三、planner搭建

        

import openai
from langchain.agents import AgentExecutor, initialize_agent, AgentType
from langchain_openai import ChatOpenAI
from langchain.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.messages import SystemMessage, HumanMessage, AIMessage
from langchain_community.chat_message_histories import ChatMessageHistory
from tools import tools


class PlannerAgent:
    def __init__(self):
        # 使用有效的API密钥
        self.llm = ChatOpenAI(
            model="gpt-4",
            temperature=0, # 确保使用有效密钥
        )
        self.chat_history = ChatMessageHistory()

        # 准备工具信息
        tools_str = "\n".join([f"{tool.name}: {tool.description}" for tool in tools])
        tool_names_str = ", ".join([tool.name for tool in tools])

        # 创建改进的系统消息
        system_content = (
            "你是一个法律助手,必须使用对话历史和提供的工具回答用户问题。\n"
            "重要提示:以下是完整的对话历史,你必须仔细阅读并利用这些信息:\n"
            "{chat_history}\n\n"  # 关键:直接嵌入历史记录位置

            "思考步骤要求:\n"
            "1. 首先分析对话历史中的上下文\n"
            "2. 然后考虑当前用户问题\n"
            "3. 如果需要,选择合适的工具\n"
            "4. 最后给出最终答案\n\n"

            "响应格式:\n"
            "Thought: ...(必须引用对话历史中的具体内容)\n"
            "Action: 工具名称(可选)\n"
            "Action Input: 工具输入参数(可选)\n"
            "Observation: 工具返回结果(可选)\n"
            "Final Answer: 最终回答\n\n"

            f"可用工具:\n{tools_str}\n\n"
            f"工具名称列表: {tool_names_str}"
        )

        # 构建提示模板
        prompt = ChatPromptTemplate.from_messages([
            SystemMessage(content=system_content),
            ("human", "{input}"),
            MessagesPlaceholder(variable_name="agent_scratchpad")
        ])

        # 创建代理执行器
        self.agent_executor = initialize_agent(
            tools=tools,
            llm=self.llm,
            agent=AgentType.STRUCTURED_CHAT_ZERO_SHOT_REACT_DESCRIPTION,
            verbose=True,
            handle_parsing_errors=True,
            agent_kwargs={
                "prefix": system_content,
                "input_variables": ["input", "chat_history", "agent_scratchpad"],
                "prompt": prompt
            }
        )

    def run(self, input_text: str) -> str:
        try:
            # 格式化为字符串的历史记录
            formatted_history = self._format_history()

            # 调用执行器 - 传入格式化后的历史
            result = self.agent_executor.invoke({
                "input": input_text,
                "chat_history": formatted_history  # 传入格式化后的历史
            })

            # 更新聊天历史
            self.chat_history.add_user_message(input_text)
            self.chat_history.add_ai_message(result["output"])

            return result["output"]
        except openai.RateLimitError as e:
            error_msg = "API配额不足,请检查账户设置"
            self.chat_history.add_ai_message(error_msg)
            return error_msg
        except Exception as e:
            error_msg = f"处理请求时出错: {str(e)}"
            self.chat_history.add_ai_message(error_msg)
            return error_msg

    def _format_history(self) -> str:
        """将对话历史格式化为字符串"""
        history_str = ""
        for msg in self.chat_history.messages:
            if isinstance(msg, HumanMessage):
                history_str += f"用户: {msg.content}\n"
            elif isinstance(msg, AIMessage):
                history_str += f"助手: {msg.content}\n"
        return history_str.strip()


if __name__ == "__main__":
    print(" 正在加载本地向量数据库...")
    planner = PlannerAgent()
    print("法律智能助手 - 多轮对话模式")
    print("请输入你的问题,输入 'exit' 或 'quit' 退出")

    while True:
        user_input = input("你: ")
        if user_input.lower() in ['exit', 'quit']:
            break

        response = planner.run(user_input)
        print(f"助手: {response}")

你可能感兴趣的:(langchain,语言模型,python,人工智能,chatgpt,ai)