1. 项目概述:构建HITL AI代理的技术全景
在当今AI应用爆炸式增长的时代,纯自动化系统面临的最大挑战是如何处理复杂决策中的不确定性。我在法律科技领域工作时,曾遇到一个典型案例:当AI系统需要解释模糊的合同条款时,完全自主的决策可能导致灾难性后果。这正是人机协同(Human-in-the-Loop, HITL)架构的价值所在——通过将人类专业判断嵌入AI工作流,实现"机器效率"与"人类智慧"的完美结合。
Elasticsearch 9.x作为核心搜索引擎,在此类系统中扮演着"记忆中枢"的角色。最新版本提供的向量搜索、混合检索能力,使其成为处理非结构化法律文本的理想选择。而LangGraph作为新兴的工作流编排框架,其基于有向无环图(DAG)的设计模式,特别适合构建需要条件分支和循环迭代的HITL系统。当用户问"LangGraph和LangChain有什么区别"时,最直观的答案是:LangChain像线性流水线,而LangGraph更像可编程的流程图。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 技术栈深度解析
2.1 Elasticsearch 9.x的关键能力
在HITL系统中,Elasticsearch远不止是搜索引擎。我们利用其三项核心能力:
- 混合检索:结合BM25算法(处理精确术语匹配)和kNN向量搜索(处理语义相似度),在legal_precedents索引中配置自定义analyzer处理法律术语的特殊分词需求
- 上下文增强:通过runtime fields在查询时动态计算相关度权重,例如给近期判例更高权重
- 安全隔离:使用Document Level Security确保律师只能访问授权案例
实测配置示例:
json复制PUT /legal_precedents
{
"settings": {
"analysis": {
"analyzer": {
"legal_analyzer": {
"type": "custom",
"tokenizer": "standard",
"filter": ["lowercase", "legal_synonym"]
}
},
"filter": {
"legal_synonym": {
"type": "synonym",
"synonyms_path": "analysis/legal_terms.txt"
}
}
}
},
"mappings": {
"properties": {
"text": {
"type": "text",
"analyzer": "legal_analyzer"
},
"embedding": {
"type": "dense_vector",
"dims": 768
}
}
}
}
2.2 LangGraph的架构设计
LangGraph的工作流由四个关键组件构成:
- 状态机(StateGraph):维护包含
user_query、retrieved_docs等字段的共享状态对象 - 节点(Node):每个节点对应一个原子操作,如
search_es节点封装Elasticsearch查询 - 边(Edge):条件边(conditional edge)实现工作流分支,如
should_request_clarification边 - 检查点(Checkpoint):支持在人工干预点保存和恢复工作流状态
与LangChain的显著差异在于循环处理能力。例如当用户提供的补充信息仍不完整时,系统可以再次触发clarification节点,形成交互循环。
3. 实战开发步骤
3.1 环境准备
推荐使用Docker Compose搭建完整环境:
yaml复制version: '3'
services:
elasticsearch:
image: elasticsearch:9.2.0
environment:
- discovery.type=single-node
- xpack.security.enabled=false
ports:
- "9200:9200"
app:
build: .
depends_on:
- elasticsearch
environment:
- ES_URL=http://elasticsearch:9200
- OPENAI_API_KEY=${OPENAI_API_KEY}
ports:
- "3000:3000"
关键依赖版本锁定:
json复制{
"dependencies": {
"@elastic/elasticsearch": "^8.12.0",
"@langchain/langgraph": "^0.0.11",
"@langchain/openai": "^0.0.12"
}
}
3.2 核心工作流实现
以法律咨询场景为例,典型流程包含:
- 初始化阶段
typescript复制const workflow = new StateGraph({
channels: {
user_query: { value: null },
retrieved_docs: { value: [] },
selected_doc: { value: null },
missing_info: { value: [] }
}
});
- 搜索节点实现
typescript复制const searchNode = async (state) => {
const client = new ElasticsearchClient();
const res = await client.search({
index: 'legal_precedents',
query: {
hybrid: {
queries: [
{ match: { text: state.user_query }},
{ knn: {
embedding: {
query_vector: await getEmbedding(state.user_query),
k: 5
}
}}
]
}
}
});
return { retrieved_docs: res.hits.hits };
};
- 条件边逻辑
typescript复制const shouldRequestClarification = (state) => {
return state.missing_info.length > 0
? "needs_clarification"
: "can_proceed";
};
4. 性能优化技巧
4.1 Elasticsearch调优
- 索引设计:对
judge_opinion字段采用rank_features类型,支持后续相关性调优 - 查询优化:对高频术语使用
span_near查询提升精度 - 缓存策略:对判例摘要启用
fielddata: true
4.2 LangGraph性能要点
- 状态压缩:对大型文档只存储
doc_id而非完整内容 - 异步处理:对LLM调用实现
Promise.all并行处理 - 超时控制:设置人工干预的TTL(Time-To-Live)
5. 典型问题排查指南
5.1 搜索相关性问题
症状:返回案例与查询意图不匹配
- 检查analyzer是否正确处理了法律术语缩写
- 验证向量嵌入模型是否适合法律领域(建议使用law-bert)
- 调整BM25与kNN的权重比例(通过
boost参数)
5.2 工作流中断问题
症状:人工干预后流程不恢复
- 检查checkpoint存储是否配置正确
- 验证状态对象的序列化/反序列化
- 确保所有节点都正确处理了
state对象
6. 扩展应用场景
6.1 金融合规审核
在反洗钱(AML)场景中,系统可以:
- 自动筛选可疑交易
- 生成风险分析报告
- 等待合规官确认后才提交SAR(可疑活动报告)
6.2 医疗诊断支持
工作流调整为:
- 检索相似病例和治疗方案
- 生成初步诊断建议
- 要求主治医生确认关键指标
- 输出最终治疗计划
我在实际部署中发现三个关键成功要素:
- 中断点设计:只在真正需要专业判断时中断流程
- 上下文保持:确保人工干预时能访问所有相关背景信息
- 版本控制:对工作流定义和ES索引结构进行严格版本管理
一个反直觉的发现是:适度降低ES搜索的召回率反而能提升整体效率,因为过量的低质量结果会增加人工筛选负担。通过调整minimum_should_match参数控制在60-70%往往能达到最佳平衡。
