该工作流有四个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模型到本地。
当然也可以根据自己的情况选择合适的模型。
该部分仅有一个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="提供法律条文的中英文互译服务"
)
该部分使用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="通过本地向量数据库搜索民法典内容及相关法律条文"
)
该部分使用本地的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都包装成了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
]
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}")