跳到主要内容

Lawsight - Legal Cases

VincentWei    2026年11月4日 13:43:16

1. 系统定位

面向律师的法律类案检索增强生成系统:输入案情描述 → 五阶段 Agentic 工作流(规划→检索→验证→撰写→引用校验)→ 输出带逐字引用、可溯源的《类案检索分析报告》。核心能力闭环:

能力 实现要点
Agentic 分析 自研星形状态机(非 LangGraph),双回路自纠错(补检 ≤2 轮 / 重生成 ≤2 轮)
停止/恢复 每阶段边界保存全量状态检查点(SQLite),pause/cancel/resume(含 failed 恢复)
结果可审计 GET /tasks/{id}/workflow 聚合五阶段产物、检查点链、事件流、思考摘要、token
引用可信 引用登记表 + 三道确定性算法校验(编号/逐字一致/无闲置),未过不交付
Token 指标 每次 LLM 调用 XADD wf:llm_tokens(9 字段),Redis 掉线落 SQLite 补发不丢
实时采集 web-search-pro 联网搜索 → GLM 结构化 → 与种子库同一管道入库;24h 连续调度
多端接入 REST(全部强制 user_id)+ Chat 意图路由 + MCP(HTTP/stdio,12 工具)

2. 总体架构图

┌──────────────────────────────────────────────────────────────────────────┐
│                              接 入 层(多端)                              │
│   ┌────────────────────┐   ┌──────────────────┐   ┌────────────────────┐ │
│   │ Vue3 前端(浏览器)  │   │ demo/run_demo.py │   │ 外部智能体          │ │
│   │ 左:Chat 中:控制      │   │ 端到端演示脚本    │   │ Claude/Cursor/通义  │ │
│   │ 右:工作台页签        │   └────────┬─────────┘   └─────────┬──────────┘ │
│   └──────────┬─────────┘            │ REST                  │ MCP        │
│              │ REST /api/v1(全部携带 user_id)              │ HTTP/stdio │
│              │ + 2.5s 轮询 events/workflow                   │            │
└──────────────┼──────────────────────┼───────────────────────┼────────────┘
               ▼                      ▼                       ▼
┌──────────────────────────────────────────────────────────────────────────┐
│                    FastAPI 单进程服务(backend/app)                       │
│                                                                          │
│   ┌──────────────┐  ┌────────────────┐  ┌─────────────────────────────┐ │
│   │ routes.py    │  │ chat.py        │  │ mcp_server.py(12 工具)     │ │
│   │ REST 全接口   │  │ 意图路由+RAG问答│  │ 工具统一经 HTTP 代理调 REST  │ │
│   └──────┬───────┘  └───────┬────────┘  └──────────────┬──────────────┘ │
│          └────────────┬─────┴──────────────────────────┘                │
│                       ▼                                                  │
│   ┌──────────────────────────────────────────────────────────────────┐  │
│   │   WorkflowEngine(engine.py · 星形状态机编排 · asyncio.Task)      │  │
│   │   Planner → Researcher → Verifier ⇄补检 → Writer ⇄重生成 → Done   │  │
│   │   (planner/researcher/verifier/writer.py,检查点+事件+控制标记)    │  │
│   └──────────┬───────────────────────────────────────┬───────────────┘  │
│              │ 四路召回+RRF+争点重排                     │ 每次调用 usage   │
│              ▼                                        ▼                  │
│   ┌───────────────────────┐             ┌───────────────────────────┐   │
│   │ CaseStore 混合索引      │             │ GLMClient(httpx 异步)    │   │
│   │ BM25│向量│法条锚点│图谱 │◄──同一入库──│ chat/chat_json/embed       │   │
│   └───────────┬───────────┘   管道       │ 重试3次+thinking参数容错    │   │
│               │                          │ embedding 哈希兜底         │   │
│   ┌───────────▼───────────┐             └─────────────┬─────────────┘   │
│   │ Collector 采集调度器    │                           ▼                 │
│   │ internet.py           │             ┌───────────────────────────┐   │
│   │ 24h 连续/间隔两模式     │             │ TokenMetrics              │   │
│   └───────────────────────┘             │ XADD + SQLite 账本 + 补发  │   │
│                                         └───────────────────────────┘   │
└──────────────────────────┬──────────────────────────────┬────────────────┘
                           ▼                              ▼
┌──────────────────────────────────┐    ┌─────────────────────────────────┐
│ 本地持久化(data/)                │    │ 外部服务                         │
│ · lawsight.db     6 张表          │    │ · 智谱 GLM 开放平台              │
│ · case_store.json 案例主数据       │    │   glm-5.3-flashx(思考型)       │
│ · case_vectors.npz 向量矩阵       │    │   embedding-3 / web-search-pro  │
└──────────────────────────────────┘    │ · Redis 7:wf:llm_tokens Stream │
                                        └─────────────────────────────────┘

分层职责:接入层只做协议适配 → API 层做 user_id 鉴权/归属校验(400/403)→ 编排层(Engine)独占任务生命周期与检查点纪律 → 领域层(检索/存储/采集/LLM)无状态可复用 → 持久化层 SQLite+JSON+NPZ 三件套。MCP 工具不直连引擎,而是回环代理 REST,保证单实例语义一致。


3. 核心机制设计

3.1 Agentic 工作流状态机(主流程图)

                 POST /tasks {user_id, fact_description}
                                │
                                ▼
        ╔═══════════════════════════════════════════════════════════════╗
        ║  主循环 while(true):取 state.next_stage → 执行 → 阶段边界         ║
        ║  ┌─────────────────────────────────────────────────────────┐  ║
        ║  │ 边界纪律:seq+1 → 保存全量状态检查点 → 读 kv:ctl:{task_id} │  ║
        ║  │  · 阶段抛异常 → 检查点(failed) + status=failed + 退出     │  ║
        ║  │  · ctl=cancel → 检查点(stopped) + status=stopped + 退出  │  ║
        ║  │  · ctl=pause  → 检查点(paused)  + status=paused  + 退出  │  ║
        ║  │  · 否则       → 检查点(running) + 事件 stage_completed   │  ║
        ║  └─────────────────────────────────────────────────────────┘  ║
        ╚═══════════════════════════════════════════════════════════════╝
                                │
        ┌──────────┐            ▼             ┌───────────┐
        │   plan   │     issues/子查询/法条候选  │ research  │  ranked(按争点top5)
        │ Planner  │─────────────────────────► │Researcher │──────────┐
        │ chat_json│  要素抽取·争点分解·HyDE     │ 四路召回+  │          │
        └──────────┘  (无效输出→单争点降级)      │ LLM 重排  │          ▼
            ▲                                     └───────────┘   ┌───────────┐
            │                                                      │  verify   │
            │      ┌──────────────────────────────┐   证据不足       │ Verifier  │
            └──────┤ revise_plan(gaps) ①补检回路   │◄─round<2───────│ 组装证据包 │
                   │ 子查询拼接gaps·放宽案由过滤    │   耗尽→徽章      │ 充分性判定 │
                   └──────────────────────────────┘  「证据不足」     └─────┬─────┘
                                                                         │ 达标
                                     ┌──────────┐  报告+regen_hints      ▼
                                     │  write   │◄──────────────┐  ┌───────────┐
                                     │ Writer   │ 受约束生成      │  │  check    │
                                     │ 协议:「…」│[n] 逐字摘录    │  │ 三道校验   │
                                     └────┬─────┘                │  │(确定性算法)│
                                          └──────────────────────►└─────┬─────┘
                                              ②重生成回路 regen<2       │ 通过
                                     (hints=问题清单前10条)              ▼
                                                                ┌───────────┐
                                                                │   done    │
                                                                │ 质量徽章:  │
                                                                │ 完整/部分 │
                                                                │ 降级/证据 │
                                                                │ 不足      │
                                                                └───────────┘

要点:next_stage 是状态机唯一游标(随检查点持久化);两条自纠错回路都有轮次上限(max_research_rounds=2 / max_regen_rounds=2),耗尽后降级交付并打徽章(绝不静默降质)。check 阶段复用 Verifier 的 verify_citations。

3.2 检查点与暂停/恢复(时序图)

 用户        API/Engine              SQLite                     Vue3 前端
  │ POST /tasks │                       │                          │
  │────────────►│ create_task+事件       │                          │
  │             │ engine.start ─► asyncio.Task(后台跑状态机)        │
  │             │                       │                          │
  │ POST /pause │                       │                          │
  │────────────►│ kv_set ctl:{id}=pause  │   ← 只设标记,不杀协程     │
  │◄────────────┤ 202"阶段边界生效"       │                          │
  │             │   …当前阶段继续执行…    │                          │
  │             │ 阶段边界 ─────────────►│ save_checkpoint(paused)  │
  │             │                       │ status=paused            │
  │             │                       │ event: paused_at_ckpt ──►│ ⏸ 对话流播报
  │             │      (服务重启:孤儿 running → paused,同样可恢复)    │
  │             │                       │                          │
  │ POST /resume│                       │                          │
  │────────────►│ latest_checkpoint ───►│ state=全量JSON快照        │
  │             │ state._resumed_from=ck.stage                      │
  │             │ engine.start(task_id, state) 从 next_stage 续跑    │
  │             │                       │ event: resumed_from ────►│ ▶ 恢复播报

设计取舍:pause 请求不打断进行中的阶段(LLM 调用不浪费、状态不撕裂),在边界原子生效;恢复 = 读最近检查点全量 state 重建执行现场。支持 paused/stopped/failed 三态恢复。

3.3 四路混合检索与融合重排(检索设计图)

                     Planner 检索计划(retrieval_plan)
        subqueries(legal_rewrite法言法语 + hyde_snippet假设文书) + statutes + filters
                                   │
          ┌────────────┬───────────┴──────────┬─────────────────────┐
          ▼            ▼                      ▼                     ▼
    ┌──────────┐ ┌───────────┐      ┌────────────────┐    ┌────────────────┐
    │ 路1 BM25 │ │ 路2 向量   │      │ 路3 法条锚点反查 │    │ 路4 案例图谱扩展│
    │ 中文bigram│ │ embedding-3│     │ 引用同一法条的   │    │ 种子案例同案由  │
    │ 纯Python  │ │ numpy余弦  │     │ chunk 集合      │    │ +共同法条邻接  │
    │ 倒排      │ │ (≤3条语义路)│    │ (精确+模糊容错) │    │ (权重1:2)      │
    └─────┬────┘ └─────┬─────┘      └───────┬────────┘    └───────▲────────┘
          └────────────┴──────┬─────────────┴─────────────────────┘
                                ▼
                     RRF 融合#1  score=Σ 1/(60+rank)
                                │ top10 案例 = 图谱扩展种子 ──────────┘
                                ▼
                     RRF 融合#2(四路全量,保留60)
                                ▼
                     结构化后过滤(case_type / cause)
                                ▼
                争点级重排:LLM 对 争点×30候选块 打 0-100 分
                (LLM 失败/漏评 → 词面重合度兜底,重排不中断)
                                ▼
            ranked = {issue_id: [top5 × {chunk_id, score, reason}]}

RRF 两段式融合是关键设计:图谱扩展依赖前三轮的融合结果选种子,因此拆成"三路融合→选种→四路融合"两步。

3.4 证据包与引用可信链路(防幻觉设计图)

   ranked(争点×证据块)                        Writer 提示词(受约束生成协议)
        │                                          ▲
        ▼                                          │ 硬性协议:
  ┌────────────────────┐    quote = 块文本前200字    │ · 事实陈述必须带 [n]
  │ 证据包 + 引用登记表  │ ────────────────────────► │ · 引文「…」[n] 必须是
  │ registry:          │      by_issue: 争点→编号    │   登记引文逐字摘录
  │  [n] → 案号/块ID/   │      statutes: 法条现行原文  │ · 证据不足显式声明
  │  seg/逐字quote/URL/ │                            │ · 不得编造案例案号法条
  │  案由/重排理由       │                            ▼
  └─────────▲──────────┘                        生成的报告
            │                                          │
            │        确定性算法逐字比对(非模型判断)      ▼
            └─────────────────────────────  ┌─────────────────────┐
                                            │ check · 三道校验      │
                                            │ ① [n] 编号在登记表内  │
                                            │ ② 「引文」⊆ 登记quote │
                                            │    (忽略空白;相似度   │
                                            │     SequenceMatcher) │
                                            │ ③ 登记引文无闲置      │
                                            └──────────┬──────────┘
                                       未通过+regen<2 ──► write(附问题清单)
                                       通过 ──► done(徽章:完整 ✓)
                                       耗尽 ──► 徽章:部分降级 + 问题清单

机制基础:登记表 quote 构造时就取自块文本的逐字子串,因此"②逐字一致"这道确定性校验在数学上可 100% 拦截编造引文(溯源优先原则)。

3.5 Token 指标管道(需求 5 数据结构)

 任意 LLM 调用面                        metrics.emit()(同步、不阻塞调用方)
 chat / chat_json /                     │
 embed / web-search-pro ──usage────────►│
                                        ├──► XADD wf:llm_tokens
                                        │    MAXLEN ~100000(近似裁剪)
                                        │    EntryID + 9 字段:
                                        │    ts/user_id/matter_id/stage/
                                        │    name/model/tokens_in/
                                        │    tokens_out/total
                                        │         │
                                        │         ▼  每次成功后 30s 节流
                                        ├──► SQLite token_usage(本地账本,
                                        │    redis_ok=0/1 标记是否已入 Stream)
                                        │         │
                                        │    Redis 恢复后:
                                        │    pending(redis_ok=0) ──补发XADD──► 置 ok
                                        │         │
                                        ▼         ▼
                             读取接口:XREVRANGE(新→旧,可按 user 过滤)
                             聚合接口:SQLite GROUP BY stage / model

双写设计:Redis Stream 是需求要求的消息队列语义(可回放/可 XREAD 实时消费),SQLite 账本保证 Redis 掉线期间指标不丢(补发机制),同时支撑聚合统计。

3.6 实时互联网采集管道(流程图)

  CollectScheduler(后台 asyncio.Task,启动延迟 10s)
  ├─ 连续模式(默认):一轮全主题大清扫 → 冷却 21600s(6h) → 下一轮,永不停轮
  └─ 间隔模式:AUTO_COLLECT_MINUTES 定时
        │  每轮 = 14 个主题 × 轮换查询变体(kv:collect_topic_index 记游标)
        │  单主题 try/except:失败只记数,不中断本轮
        ▼ 每主题
  ┌───────────────┐   ┌──────────────────┐   ┌───────────────┐   ┌──────────┐
  │ web-search-pro │──►│ GLM 结构化整理    │──►│ CaseRecord    │──►│ 同一入库  │
  │ POST /tools    │   │(提示词禁编造,   │   │ 案号防伪:无案号│   │ 管道      │
  │ 实时联网搜索    │   │ 忠实搜索结果)    │   │ → WEB-md5(url)│   │ add_case:│
  │ title/link/    │   │ 截断JSON →       │   └───────────────┘   │ 分块→向量 │
  │ content        │   │ _salvage_items   │                       │ →BM25/图谱│
  └───────────────┘   │ 对象级抢救        │                       │ →持久化  │
                      └──────────────────┘                       └──────────┘
        │
        └──► collected_items 表(来源/URL/摘要/raw,全程可溯源)
             采集本身消耗的 token 也 emit 到 wf:llm_tokens(stage=collect.*)

采集数据与 17 个种子案例走完全相同的入库管道(store.add_case() 单一入口),采集结果立即可被四路召回命中、进入分析证据。

3.7 对话式入口与中间思考展示(交互设计图)

 POST /api/v1/chat {user_id, message, attachment?}
        │
        ▼
  GLM 意图分类(chat.route,输出 JSON)
        │
        ├─ create_task ──► 附件全文+消息组装为案情 → 建任务 → engine.start
        ├─ pause/resume ──► 作用于该用户最近任务(engine 控制接口,语义同 REST)
        ├─ cancel/status ──► 同上;status 汇报 阶段/检查点/token 摘要
        ├─ collect ──────► collect_from_internet(query) → 播报入库结果
        └─ qa ──────────► store.search_cases(top5) + 附件节选
                          → QA_PROMPT 受约束 JSON 回答(≤300字,附案号)

  中间思考展示链路(需求:实时播报):
  LLM reasoning_content ─► record_thinking() ─┬─ state["thinking"][key](随检查点持久化,
                                               │   workflow 接口可见,≤600字)
                                               └─ events 表 agent_thinking 事件(≤400字)
                                                      │
  前端 2.5s 轮询 /tasks/{id}/events ─────────────────┘
       eventToMsg() 将 阶段完成/暂停/恢复/思考/失败 翻译为对话流气泡 💭

3.8 数据模型与持久化(存储设计图)

  SQLite lawsight.db(单连接+RLock 线程安全)         CaseStore 内存索引 + 文件
  ┌───────────────┐  1:N  ┌────────────────┐          ┌──────────────────────────┐
  │ tasks          │──────►│ checkpoints    │          │ cases{案号→CaseRecord}   │
  │ task_id PK     │       │ seq/stage/     │          │ chunks{cid→EvidenceChunk}│
  │ user_id/matter │       │ status/state   │          │ laws{法条名+条号→LawArticle}│
  │ status/stage/  │       │ (TaskState全量  │          │ bm25(倒排+df+avgdl)    │
  │ report/error   │       │  JSON 快照)     │          │ vectors{cid→2048维}      │
  └───────┬───────┘       └────────────────┘          │ statute_index 法条→块集合 │
          │ 1:N           ┌────────────────┐          │ cause_graph  案由→案例集 │
  ┌───────▼───────┐       │ events         │          └────────────┬─────────────┘
  │ token_usage    │       │ (审计流,chat  │                       │ 增量持久化
  │ 本地账本       │       │  播报的数据源)  │          case_store.json(案例+法条主数据)
  │ redis_ok 标记  │       └────────────────┘          case_vectors.npz(向量,重启零成本恢复)
  └───────────────┘       ┌────────────────┐
  ┌───────────────┐       │ collected_items │          TaskState(随检查点持久化的内存态):
  │ kv             │       │ 采集溯源账本    │          争点/计划/召回/证据包/报告/轮次/
  │ ctl:{task_id}  │       └────────────────┘          thinking/stage_timings/history/
  │ 控制标记        │                                  next_stage(恢复游标)
  │ 采集主题游标    │
  └───────────────┘
最近更新: 2026年11月4日 13:43:16
浏览: 12

[[total]] 条评论

添加评论
  1. [[item.time]]
    [[item.user.username]] [[item.floor]]楼
  2. 点击加载更多……
  3. 添加评论