在法律咨询和案例分析领域,律师经常需要查阅大量判例来支持他们的案件论证。传统的人工检索和分析过程耗时费力,而纯AI系统又难以保证关键法律判断的准确性。本文将介绍如何结合LangGraph和Elasticsearch构建一个高效且可靠的人机交互(HITL)系统,在法律案例分析场景中实现智能决策支持。
这个系统的核心价值在于:
Elasticsearch作为业界领先的搜索引擎,在处理法律判例这类非结构化文本数据时具有明显优势:
LangGraph则提供了构建复杂AI工作流的能力:
整个系统采用分层架构设计:
code复制前端界面/CLI
↓
工作流引擎层(LangGraph)
↓
AI服务层(LLM+Embeddings)
↓
数据存储层(Elasticsearch)
关键组件交互流程:
硬件要求:
软件依赖:
安装核心依赖包:
bash复制npm install @elastic/elasticsearch @langchain/community @langchain/core @langchain/langgraph @langchain/openai dotenv --legacy-peer-deps
npm install --save-dev tsx
环境变量配置(.env文件):
code复制ELASTICSEARCH_ENDPOINT=your_es_endpoint
ELASTICSEARCH_API_KEY=your_es_api_key
OPENAI_API_KEY=your_openai_key
我们使用的数据集包含多个法律判例,每个判例包含以下信息:
json复制{
"pageContent": "判例内容文本",
"metadata": {
"caseId": "唯一案例ID",
"contractType": "合同类型",
"delayPeriod": "延迟期限",
"outcome": "判决结果",
"reasoning": "法院推理",
"keyTerms": "关键词",
"title": "案例标题"
}
}
数据导入关键步骤:
javascript复制const indexSettings = {
settings: {
analysis: {
analyzer: {
default: {
type: "standard"
}
}
},
number_of_shards: 1,
number_of_replicas: 0
},
mappings: {
properties: {
vector: {
type: "dense_vector",
dims: 1536 // OpenAI嵌入维度
},
text: { type: "text" },
metadata: {
properties: {
caseId: { type: "keyword" },
contractType: { type: "keyword" },
// 其他字段映射...
}
}
}
}
};
javascript复制async function bulkInsert(documents) {
const body = documents.flatMap(doc => [
{ index: { _index: INDEX_NAME } },
{
text: doc.pageContent,
metadata: doc.metadata,
vector: await getEmbedding(doc.pageContent)
}
]);
await esClient.bulk({ refresh: true, body });
}
注意:在实际生产环境中,建议使用Elasticsearch的批量处理API并添加适当的错误处理和重试机制。
我们使用TypeScript接口定义工作流状态:
typescript复制interface LegalResearchState {
query: string; // 用户原始查询
analyzedConcepts: string[]; // 从查询中提取的关键概念
precedents: Document[]; // 检索到的相关判例
selectedPrecedent: Document | null; // 用户选择的判例
draftAnalysis: string; // 初步分析结果
ambiguityDetected: boolean; // 是否需要更多信息
userClarification: string; // 用户提供的额外信息
finalAnalysis: string; // 最终分析报告
}
javascript复制async function searchPrecedents(state) {
console.log("Searching for relevant legal precedents...");
// 使用Elasticsearch向量搜索
const results = await vectorStore.similaritySearch(state.query, 5);
// 格式化输出结果
results.forEach((doc, i) => {
const meta = doc.metadata;
console.log(`${i+1}. ${meta.title} (${meta.caseId})`);
console.log(` 合同类型: ${meta.contractType}`);
console.log(` 判决结果: ${meta.outcome}`);
console.log(` 关键推理: ${meta.reasoning}\n`);
});
return { precedents: results };
}
javascript复制async function selectPrecedent(state) {
const userInput = state.userChoice;
const precedents = state.precedents;
// 使用结构化输出确保返回有效选择
const structuredLlm = llm.withStructuredOutput({
schema: {
type: "object",
properties: {
selected_number: {
type: "number",
minimum: 1,
maximum: precedents.length
}
}
}
});
const response = await structuredLlm.invoke(
`用户输入: "${userInput}"\n` +
`可选判例:\n${formatPrecedentsList(precedents)}\n` +
`请选择最相关的判例编号(1-${precedents.length}):`
);
return {
selectedPrecedent: precedents[response.selected_number - 1]
};
}
javascript复制async function createDraft(state) {
const precedent = state.selectedPrecedent;
// 使用LLM生成初步分析
const response = await llm.invoke([
{
role: "system",
content: "你是一个法律研究助手,负责分析案例并识别需要额外上下文的情况。"
},
{
role: "user",
content: `基于以下判例:\n${formatPrecedent(precedent)}\n` +
`回答这个问题:"${state.query}"\n` +
"如果需要更多合同条款或上下文信息才能提供准确分析,请明确指出。"
}
]);
// 解析响应,判断是否需要更多信息
const needsClarification = detectAmbiguity(response.content);
return {
draftAnalysis: response.content,
ambiguityDetected: needsClarification
};
}
工作流中设计了两个人机交互点:
javascript复制function precedentSelection(state) {
console.log("\n需要人工输入:请选择最相关的判例\n");
const userChoice = interrupt({
question: "哪个判例与您的案件最相似?"
});
return { userChoice };
}
javascript复制function requestClarification(state) {
console.log("\n需要更多信息才能完成分析\n");
const clarification = interrupt({
question: "请提供关于合同条款的更多细节:"
});
return { userClarification: clarification };
}
javascript复制const workflow = new StateGraph(LegalResearchState)
.addNode("analyzeQuery", analyzeQuery)
.addNode("searchPrecedents", searchPrecedents)
// 添加其他节点...
.addEdge("__start__", "analyzeQuery")
.addEdge("analyzeQuery", "searchPrecedents")
// 添加其他边...
.addConditionalEdges(
"createDraft",
(state) => state.ambiguityDetected ? "needsClarification" : "final",
{
needsClarification: "requestClarification",
final: "generateFinalAnalysis"
}
);
执行一个完整的法律问题分析:
code复制========================================================================
⚖️ 最终法律分析
========================================================================
基于判例H和您提供的合同细节,分析如下:
1. 法律原则:
- 重复小额延迟的累积效应可能构成违约
- 合同中的"及时交付"条款虽未定义具体时间,但建立了合理时间预期
2. 您的具体情况:
- 6个月内8次延迟显示了一种模式
- 延迟导致实际业务影响(客户截止日期错过)
- 已通知供应商但问题持续
3. 建议行动:
- 收集所有延迟记录和影响证明
- 正式通知供应商违约情况
- 考虑合同救济选项:
* 要求赔偿损失
* 协商修订交付条款
* 如持续违约,考虑终止合同
4. 预防措施:
- 未来合同应明确定义交付时间表
- 加入延迟处罚条款
- 建立定期绩效评估机制
索引优化:
查询优化:
javascript复制const searchQuery = {
query: {
bool: {
must: {
knn: {
vector: {
vector: await getEmbedding(query),
k: 5
}
}
},
filter: {
term: { "metadata.contractType": "service agreement" }
}
}
},
size: 5
};
系统可以轻松扩展到其他法律场景:
扩展工作流只需添加新的节点和边:
javascript复制workflow
.addNode("checkCompliance", checkCompliance)
.addEdge("generateFinalAnalysis", "checkCompliance");
中断时机的选择:
提示设计原则:
输入验证:
数据建模:
查询优化:
性能监控:
可视化工作流:
状态检查:
错误处理:
问题1:向量搜索返回不相关结果
问题2:查询性能慢
问题1:工作流在错误点中断
问题2:状态管理混乱
问题1:用户输入难以解析
问题2:交互流程不顺畅
多轮对话支持:
多模态输入:
知识图谱集成:
缓存策略:
异步处理:
分布式扩展:
医疗领域:
金融领域:
教育领域:
在实际使用这个系统的过程中,我发现最有价值的不是完全的自动化,而是在合适的节点引入人工判断。这种协同工作模式既保留了人类专业判断的优势,又充分利用了AI处理大量信息的能力。特别是在法律这种高风险的领域,保持"人在循环中"的设计理念尤为重要。