跳到主要内容

Lawsight - Patent Deep Analysis

VincentWei    2026年11月3日 20:52:00

AI 专利分析多智能体系统:chat-first 对话入口 + 工作台可视化,支持专利无效性检索与侵权取证(EOU)两种分析。产出为律师工作底稿,每条结论带逐字引用、可溯源、可核验。


无效检索

全景检索状态机

检索过程建模为状态机,状态包括:INIT(解析完成,生成初始计划)→ PLAN(查询计划 k 版)→ EXECUTE(并行多通道召回)→ ASSESS(要素级评估矩阵更新)→ GAP(缺口识别)→ 循环回到 PLAN,直至满足终止条件。终止条件三选一:全部独立权利要求都存在至少一个 §102 候选或完整 §103 组合;迭代收益递减(新增覆盖要素数连续两轮为 0);预算耗尽(时间/ token / 调用数)。状态机每轮更新黑板上的覆盖矩阵 M[element, reference],该矩阵既是算法状态也是向用户展示检索范围的可视化数据源(对应官网:可以看到已经检索了什么)

覆盖率保障机制

全景是渐近目标,工程上用四个机制逼近。机制一,多视角查询:同一要素至少从术语、功能、问题—解决方案三个视角各生成一组查询,防止单一表述造成的检索盲区。机制二,跨域类比通道:对每个要素抽取功能语义向量(what it does,剥离领域名词),在去掉本领域 CPC 过滤的条件下做向量检索,专门召回类比文献——针对 Stilta 披露的第一类失败模式。机制三,组合优先搜索:组合求解器优先选择能覆盖最多未覆盖要素的文献(集合覆盖贪心 + 局部替换),使 §103 路径尽早闭合。机制四,不确定性上报:当某要素在 N 轮后仍无覆盖,将其标记为需要专家判断的硬缺口并在报告中显式披露,而非静默放弃——与 Stilta 的透明性原则一致。

可引用结果对象(CitableFinding)

系统一切对外输出均以 CitableFinding 为原子:结构为(结论文本,要素引用,证据引用列表,论证类型 §102/§103/DoE,置信区间标注,生成模型与版本,核验状态)。每条证据引用(EvidenceCitation)包含:文献 ID 与类型、定位(页码 + 段落 + 字符级 span 的 PDF 坐标)、逐字引文(verbatim quote)、公开日与合规说明、存档链接、核验结果(逐字匹配通过/警告)。核验规则:引文必须与源文件 OCR/文本层完全一致(允许空白差异),不一致即打回重新生成;日期不合规(晚于优先权日且非 §102(b) 类例外)的引用不得进入 §102/§103 论证。该对象设计直接对标官网承诺:引文逐字、直链源 PDF 页面、数秒核验。

侵权分析

第一,检索目标从文献域转向产品域:Product Discovery Agent 以要素关键词 + 技术领域在网页/电商/文档/视频/代码域检索竞品,输出候选公司-产品对,并按与全部要素的联合相关性排序(对应官网:为单个专利或整个组合发现谁在使用你的专利)。第二,证据采集器增加网页存档时间戳与版本追踪,产品页变化触发增量重评。第三,映射矩阵按三档解释分别计算:M_interpretation[element, product, evidence],claim chart 视图允许切换解释档并高亮结论翻转的要素(这是 Stilta 2026-06 功能的扩展设计)。输出物包含:逐产品 claim chart、缺口清单、证据充分性评级、监控订阅建议。

图表索引

# 图表 位置
图 1-1 系统分层架构图 §1.1
图 1-2 黑板中心数据流图 §1.2
图 1-3 部署视图 §1.3
图 2-1 Run 运行状态机 §2.1
图 2-2 事件总线扇出 §2.5
图 2-3 LLM 调用容错链 §2.4.1
图 2-4 熔断器状态机 §2.8.1
图 3-1 端到端时序图(对话发起分析) §3.1
图 3-2 无效性分析主流程 §3.2
图 3-3 侵权分析主流程 §3.3
图 3-4 Chat 意图路由流程 §3.4
图 3-5 引用核验与降级流程 §3.5
图 3-6 检查点恢复流程 §3.6
图 3-7 前端 SSE 事件处理流 §3.7

第一部分 架构设计

1.1 系统分层架构

五层架构:前端展现层 → API 网关层 → 编排层 → 智能体层 → 资源层,横切企业级关注点。

graph TB
    subgraph FE["前端展现层 · Vue3 SPA (Vite 构建, 后端 :8000 托管 dist)"]
        Shell["ShellView 双栏外壳<br/>路由 / 与 /run/:id"]
        ChatP["ChatPanel 左1/3对话流<br/>消息卡/智能体直播/会话抽屉/📎上传"]
        WB["工作台 右2/3<br/>Overview · PriorArt · ClaimChart · Findings"]
        i18n["i18n 中英双语"]
        APIJ["api.js<br/>fetch封装(X-User-Id) + SSE消费"]
        Shell --> ChatP
        Shell --> WB
        ChatP --> APIJ
        WB --> APIJ
    end

    subgraph GW["API 网关层 · FastAPI"]
        MW["中间件链<br/>RequestContext(请求ID/用户,最外层) → RateLimit(令牌桶) → CORS"]
        R1["routers/chat 对话SSE"]
        R2["routers/runs 分析CRUD+steer+resume"]
        R3["routers/conversations 会话+上传"]
        R4["routers/figures 附图代理"]
        R5["routers/stream 运行SSE + health/metrics/probe"]
        MW --> R1 & R2 & R3 & R4 & R5
    end

    subgraph ORCH["编排层"]
        ORC["Orchestrator 黑板状态机<br/>invalidity / infringement 两条流水线"]
        EVT["EventBus 进程内发布订阅<br/>+2000条/run事件历史"]
        CTX["_active_ctx 运行态上下文<br/>(steering 收件箱)"]
        ORC --> EVT
    end

    subgraph AGENTS["智能体层 · BaseAgent(预算护栏+步骤事件+法域注入)"]
        A1["IntentAgent"]
        A2["ClaimParser"]
        A3["SearchStrategist"]
        A4["EvidenceHunter"]
        A5["ElementMapper"]
        A6["CitationVerifier"]
        A7["InvalidityReasoner"]
        A8["ProductDiscovery"]
        A9["DeepSearchAgent"]
        A10["ReportGenerator"]
    end

    subgraph RES["资源层"]
        C1["GLMClient glm-5.3-flashx<br/>chat/chat_json/chat_stream"]
        C2["WebSearchClient GLM web_search"]
        C3["ZhihuiyaClient 智慧芽<br/>P002/P008/P069/P018/P012/P015/P021"]
        DB[("SQLite aiosqlite<br/>runs/agent_steps/conversations<br/>uploads/chat_messages/findings")]
        REDIS[("Redis Stream<br/>wf:llm_tokens")]
    end

    subgraph ENT["企业级横切 · enterprise/"]
        E1["resilience 重试+熔断"]
        E2["metrics Prometheus"]
        E3["middleware 限流/请求上下文"]
        E4["token_metrics token计量"]
        E5["logging_config JSON日志+contextvars"]
    end

    subgraph EXT["外部服务"]
        GLM["智谱 bigmodel API"]
        ZHY["智慧芽 connect.zhihuiya.com"]
    end

    APIJ -->|"REST + SSE"| MW
    R1 & R2 --> ORC
    ORC --> AGENTS
    AGENTS --> C1 & C2 & C3
    C1 & C2 -->|"resilient_call"| GLM
    C3 -->|"resilient_call"| ZHY
    ORC --> DB
    E4 --> REDIS
    R5 --> EVT
    EVT -->|"SSE /runs/:id/events"| APIJ
    E1 -.包裹.-> C1 & C2 & C3
    E2 -.采集.-> C1 & C2 & C3 & GW

要点

  • 中间件执行顺序(Starlette 后添加者最外层):RequestContextMiddleware(request_id/user_id contextvars)→ RateLimitMiddleware(仅 /api 路径,按客户端 IP 令牌桶)→ CORS → 路由。
  • 单实例进程内架构:EventBus、_active_ctx、token 内存聚合均为内存态;水平扩展需引入 Redis Pub/Sub(见 §4 DESIGN.md 限制)。

1.2 核心架构模式:黑板 + 事件溯源

智能体之间不直接调用,全部通过黑板(Run.blackboard JSON 列)读写协作;每次可观察状态迁移发布具名事件,前端凭事件流驱动 UI——事件既是实时通道,也是可回放的执行日志。

graph LR
    subgraph AG["智能体(只读写黑板)"]
        CP["ClaimParser"]
        SS["SearchStrategist"]
        EH["EvidenceHunter"]
        EM["ElementMapper"]
        CV["CitationVerifier"]
        IR["InvalidityReasoner"]
        PD["ProductDiscovery"]
        RG["ReportGenerator"]
    end

    BB[("黑板 Blackboard<br/>elements / queries / references<br/>mappings / coverage / gaps<br/>combinations / findings / report")]

    CP -->|"写 elements"| BB
    SS -->|"读 elements 写 queries"| BB
    EH -->|"读 queries 写 references"| BB
    EM -->|"读 elements×references 写 mappings"| BB
    CV -->|"改写 mappings.verify"| BB
    IR -->|"读 mappings 写 coverage/gaps/combinations"| BB
    PD -->|"写 product_leads"| BB
    RG -->|"写 report"| BB
    BB -->|"检查点 _checkpoint()"| DB[("SQLite runs.blackboard")]
    BB -->|"emit()"| EVT["EventBus → SSE"]

黑板带来的架构收益

  1. 可恢复:黑板逐文献落库即检查点;恢复时以 mapped_ref_ids 差集跳过已完成工作。
  2. 可观测:任一时刻 GET /runs/{id}/blackboard 得到全量中间态。
  3. 可插拔:流水线步骤是编排器里的普通 await 调用,增删步骤不改智能体。

四条架构决策记录(ADR 摘要)

决策 选择 备选与否决理由
智能体协作模型 共享黑板 消息传递:中间态难持久化、难整体恢复
映射调用粒度 每文献 1 次 LLM 覆盖全部要素 每要素 1 次:丢失全文上下文、调用数 = 要素×文献
§103 组合搜索 确定性贪心集合覆盖 + LLM 仅写动机论证 纯 LLM 搜索:不可复现不可测试
实时通道 进程内 EventBus + SSE(历史回放) WebSocket:双向能力冗余,SSE 足够且自动重连

1.3 部署视图

graph TB
    subgraph HOST["单机部署(演示形态)"]
        UV["uvicorn :8000<br/>app.main:app"]
        subgraph APP["FastAPI 进程"]
            API["/api/* 路由"]
            SSE["SSE 长连接"]
            STATIC["StaticFiles 托管 frontend/dist<br/>+ SPA 404 回退 index.html"]
            BG["asyncio.create_task / BackgroundTasks<br/>后台运行 Orchestrator"]
        end
        SQLITE[("lawsight.db")]
        LOG[("logs/app.log JSON")]
    end
    subgraph OPT["可选组件"]
        R[("Redis :6379<br/>不可用时自动降级为日志告警")]
        PROM["Prometheus 抓取 /api/metrics"]
    end
    BROWSER["浏览器"] -->|"HTTP + SSE"| UV
    UV --> SQLITE
    UV --> LOG
    UV -.XADD token流水.-> R
    PROM -.抓取.-> UV
    UV -->|"出网 HTTPS"| BIGMODEL["open.bigmodel.cn"]
    UV -->|"出网 HTTPS"| ZHY["connect.zhihuiya.com"]

开发态:前端 bun run dev(:5173) 直连 :8000(CORS 白名单含 5173);生产态:单服务 :8000 同时供 API 与静态资源,深链 /run/{id} 由 SPA 回退处理。


第二部分 详细设计

2.1 编排器与运行状态机(orchestrator.py)

Run 状态机:

stateDiagram-v2
    [*] --> created: POST /api/runs 或对话自动创建
    created --> running: /start 或对话槽位齐自动启动
    running --> awaiting_review: 流水线正常完成(99%)
    running --> stopped: steer stop → ctx.stop_requested
    running --> failed: 异常(bb.error + run_failed事件)
    stopped --> running: POST /resume 检查点恢复
    failed --> running: POST /resume 检查点恢复
    awaiting_review --> [*]: 律师审阅 / steer 引导

执行入口 execute() 的统一框架:

  1. register_ctx 登记 steering 收件箱 + matter_var 设 run_id(token 计量按 matter 归集)。
  2. 置 running、发 run_started 事件。
  3. 按 module 分派 run_invalidity() / run_infringement()。
  4. finally:写终态(stopped/awaiting_review/failed)、llm_calls、token_usage、黑板整体落库、发 run_completed、drop_ctx。

构造函数的恢复语义:黑板 setdefault 全部键;resolve_domain(jurisdictions, params.law_domain) 决定法域(US 默认,CN/EP 可选),law_prompt_block 生成注入所有系统提示的法定依据块。

进度上报协议 _progress(pct, stage, detail):写黑板 progress → _checkpoint() 落库 → 发 progress 事件。进度规划:

阶段 无效性 侵权
启动/专利加载/权利要求解析 5 / 10 / 20 5 / 10 / 20
检索计划(逐要素 30→44)/ 文献抓取 30~44 / 55 —(产品发现 35)
网页证据采集 — 55
逐要素比对(逐文献/证据 55→88) 55~88 55~88
引用核验 / 覆盖与组合(缺口分析) 88 / 92 88 / 92
报告生成 / 收尾 95 / 99 95 / 99

检查点策略:恢复场景跳过解析(黑板已有 elements);映射循环内每文献 _checkpoint();检索异常仅发 log 事件不中断;_checkpoint 自身失败静默(不中断分析)。

_backfill_why_relevant:映射完成后为每篇文献生成「公开权N·要素M…:首个 rationale」的证据表说明列,无命中时回退标题。

2.2 黑板数据结构(事实 Schema)

无强校验,靠编排器 setdefault 与智能体约定维护。字段名即 API 契约。

{
  "module": "invalidity|infringement",
  "law_domain": "US|CN|EP",
  "round": 1,                          // 当前 gap 轮次
  "patent": {                          // load_patent 产物
    "pn": "US12003836B2", "title": "", "abstract": "",
    "patent_id": "智慧芽ID", "assignee": "", "pbdt": "公开日(检索截止)", "claims": [{"num":"1","text":"..."}]
  },
  "elements": [{                       // ClaimParser 产物
    "id": "md5(claim:order)[:12]", "claim_num": "1", "order": 1,
    "text": "要素原文 verbatim",
    "interp_broad": "…", "interp_narrow": "…", "interp_proposed": "…",   // 三档解释
    "key_terms": ["中英关键词≤8"], "function_summary": "剥离领域名词的功能语义",
    "is_112f": false
  }],
  "queries": [{ "element_id": "…", "perspective": "terms|functional|problem_solution|eou",
                "query": "…", "rationale": "…", "round": 1 }],
  "references": [{                     // 统一文献模型(三来源)
    "id": "md5(pn|url)[:12]", "source": "zhihuiya_patent|web|user_added|user_upload",
    "pn": "", "url": "", "title": "", "assignee": "", "pbdt": "", "apdt": "",
    "relevancy": "", "note": "", "text": "全文(claims≤9000|abstract≤4000|web≤6000)",
    "text_kind": "claims|abstract|web_page|user_supplied|user_upload",
    "why_relevant": "回填", "figure": {"url": "签名URL", "ts": 0}
  }],
  "mappings": [{ "element_id": "", "ref_id": "", "verdict": "met|not_met|uncertain",
                 "quote": "逐字摘录", "locus": "权利要求1|网页正文",
                 "rationale": "", "verify": "pass|warn|fail", "verify_reason": "" }],
  "coverage": { "<element_id>": [{"ref_id":"","quote":"","locus":""}] },   // 仅 met 且 verify∈{pass,warn}
  "gaps": ["<element_id>"],
  "combinations": [{ "primary_ref": "", "secondary_refs": [], "covered_elements": [],
                     "missing_elements": [], "motivation": "", "weakness": "",
                     "confidence": "low|medium|high" }],
  "s102_ref": "<ref_id>|null",
  "product_leads": [{ "company": "", "product": "", "evidence_hint": "",
                      "keywords_matched": [], "confidence": "" }],
  "findings": [{ "element_id": "", "reasoning_type": "s102|s103|gap|evidence_of_use",
                 "statement": "", "confidence": "high|medium|low", "verified": true,
                 "citations": [{"ref_id":"","quote":"","pn":"","url":"","locus":""}], "score": 0.0 }],
  "report": { "summary": "", "conclusions": [{"claim":"","basis":"","confidence":""}],
              "risks": [], "next_actions": [] },
  "progress": { "pct": 0, "stage": "", "detail": "" },
  "token_usage": { "calls": 0, "in": 0, "out": 0, "total": 0 },
  "active_interpretation": "interp_proposed",   // steer 写入
  "focused_elements": []                         // steer 写入
}

2.3 智能体详细设计

BaseAgent 公共契约(agents/base.py):

  • llm_json / llm_text:先查 budget_left()(params.llm_max_calls_per_run,默认 60,超限抛 RuntimeError)→ 计数 + 发 log 事件(purpose + 第 N 次调用)→ stage_var 注入计量 → 系统提示前缀 law_block。
  • step(action, detail, status):发 agent_step 事件 + 结构化日志。
  • AgentContext:run_id / module / bb / params / llm_calls / stop_requested / notes。
智能体 输入 输出 提示词要点 容错
IntentAgent 近 8 轮对话 + 当前消息 {intent, module, patent_number, target_company, jurisdictions, invention_disclosure, notes, law_domain, missing_slots⊆[module,patent_input], conversational_reply} 五意图;槽位跨轮累积;一次只追问一项 LLM 失败→_rule_intent 关键词规则;专利号正则 (US\|CN\|EP\|WO\|JP\|KR\|DE\|GB\|FR)[\s-]?\d{5,}[\s-]?[A-Z]\d? 兜底;module 关键词规则兜底
ClaimParser 独权(≤max_independent_claims=2)+ 标题 elements[](≤max_elements=8/项) 一要素=主语+动作+对象;原文 verbatim;三档解释无前缀 空要素→加强指令重试 1 次;无效 claim/element 剔除
SearchStrategist elements(缺口轮仅 gaps) plans[]:每要素 ≤3 视角查询 terms/functional/problem_solution 三视角;缺口轮换表述 element_id 白名单过滤
EvidenceHunter.run_patents 候选(pn 去重、剔自身) references(带全文) — P018 批量(100/块)→逐条降级;P012 补齐无权要求文献;仍无文本则丢弃
EvidenceHunter.run_web 查询列表 web references — 单查询失败跳过;URL 去重;content 为空丢弃
ElementMapper.map_reference elements × 单篇文献全文(≤9000) mappings[](每要素恰一条) 反幻觉硬约束:quote 逐字连续片段;无原文→not_met/uncertain 未知 element_id 剔除;verdict 非法→uncertain;缺失补 not_met
CitationVerifier mappings + refs_by_id 打写 verify/verify_reason 纯算法无 LLM 详见 §3.5
InvalidityReasoner elements + mappings + refs coverage/gaps/combinations/s102_ref 动机论证 Graham/KSR + teaching-away 弱点,仅可引给定材料 动机 LLM 失败→degraded,组合保留空 motivation
ProductDiscovery elements + 标题 + 目标公司 leads ≤6 仅保留有真实公开信息支撑的线索 LLM 失败→检索结果直出 4 条低置信线索
DeepSearchAgent question {answer, sources≤14} plan 2~4 查询;综合时标注 [n] 不编造 第2轮只补缺口;单查询失败继续
ReportGenerator 分析结果 report{summary/conclusions/risks/next_actions} 结论必须标注支撑文献;区分引用支撑 vs 推断 —

InvalidityReasoner 组合搜索算法(确定性,测试锁定):

  1. build_coverage:仅 verdict=met 且 verify∈{pass,warn} 的映射计入覆盖。
  2. search_combinations:构建 ref→覆盖要素集合;按覆盖数排序取前 2 个主参考;束搜索(beam=24,agenda 安全阀 400)扩展次要文献;得分 len(covered) − 0.15×(refs−1);max_refs=3;找到首个全覆盖组合即止。
  3. §102 快查:任一单篇覆盖全部要素 → s102_ref。
  4. 最优组合再调 LLM 写 motivation/weakness/confidence。

2.4 客户端层详细设计

2.4.1 GLMClient:推理模型适配 + JSON 容错链

flowchart TD
    A["agent.llm_json(system, user)"] --> B{"budget_left()?"}
    B -->|"否"| B1["抛 RuntimeError 预算耗尽"]
    B -->|"是"| C["llm_calls+=1<br/>发log事件 / stage_var注入"]
    C --> D["chat(): _chat_once"]
    D --> E["resilient_call<br/>重试3次+退避+熔断(glm)"]
    E --> F{"空内容 或<br/>finish_reason=length?"}
    F -->|"是(思考耗尽预算)"| G["2× max_tokens 重试一次"]
    F -->|"否"| H["parse_json_block"]
    G --> H
    H --> I{"解析成功?"}
    I -->|"是"| J["返回结构化结果"]
    I -->|"否"| K["4× max_tokens 再调一次"]
    K --> L["parse_json_block 容错链"]
    L --> M{"成功?"}
    M -->|"是"| J
    M -->|"否"| N["抛 ValueError"]
    subgraph PCB["parse_json_block 容错链(顺序)"]
        P1["① ```json 围栏提取"] --> P2["② 整体 json.loads"]
        P2 --> P3["③ 首个平衡对象/数组截取"]
        P3 --> P4["④ 内层裸引号重转义<br/>(引号后非结构符则视为内容)"]
        P4 --> P5["⑤ 截断修复:回退裁切边界≤64<br/>去悬挂逗号/裸key(仅对象)→补全括号"]
    end
    L -.-> PCB

测试 test_core.py 锁定:围栏/前缀包裹/纯截断/裸引号/引号+截断叠加/数组尾元素保留/2×重试预算序列 [100,200]。

2.4.2 WebSearchClient:查询降级梯

search_std 对全词 AND——LLM 生成的 7~9 词长查询常 0 结果。0 结果时依次重试:去引号取前4词 → 前2词(源码注释标注 2026-11-02 实测验证)。已试集合去重。

2.4.3 ZhihuiyaClient:API 映射与批量

端点 用途 关键实现
P069 POST /search/patent/patent-search-pn pn→patent_id 不支持逗号批量(实测),只取首条
P002 POST …/query-search-patent/v2 检索式通道 query_text/limit/offset
P008 POST …/semantic-search-patent/v2 语义通道(要素级稠密检索) text + pbd_to 公开日截止(防 EPO 后公开)
P018 GET /basic-patent-data/claim-data 权利要求 HTML 批量逗号连接(100/块);indep-clm class + num 属性拼接独立权利要求→纯文本
P012 GET /basic-patent-data/bibliography 题录 嵌套 bibliographic_data 解析,EN 优先,扁平字段兜底
P015 GET …/forward-citation/v3 引用图扩展 (客户端已备,流水线未用)
P021 GET …/abstract-image 摘要附图签名 URL figures 路由专用

统一响应约定:status=false → ZhihuiyaError(error_msg, error_code)(如余额不足→运行快速失败)。

2.5 事件系统与 SSE 协议(events.py / stream.py)

graph LR
    subgraph SRC["发布方"]
        ORC["Orchestrator<br/>progress/run_*/steering"]
        AG["Agents<br/>agent_step/log/…"]
        CHT["chat.py<br/>chat_message"]
    end
    EMIT["emit(run_id, type, **data)"]
    BUS["EventBus<br/>history≤2000/run(超裁至1000)<br/>subscribers: Set&lt;Queue&gt;"]
    SSE1["GET /runs/:id/events<br/>from_index 回放 + 15s 心跳"]
    FE["前端 subscribeRunEvents<br/>EventSource 自动重连"]
    SRC --> EMIT --> BUS
    BUS --> SSE1 --> FE
    BUS --> HIST["GET /runs/:id/events/history<br/>(运维排查)"]

事件清单与载荷:

事件 data 关键字段 前端行为
run_started module, patent_number —
progress pct, stage, detail 进度条;stage 变化→对话直播行
agent_step agent, action, status, detail status≠ok→直播行
log agent, message 直播行(每次 LLM 调用可见)
patent_loaded / elements_ready patent / elements 后者刷新面板+直播
reference_added reference(完整文献) 刷新+直播
coverage_updated coverage, gaps, coverage_pct 刷新+直播
citations_verified verified, failed —
combinations_ready combinations, s102_ref —
products_discovered leads —
steering_applied action, payload, applied —
run_resumed checkpoint{elements,references,mappings} —
run_completed / run_failed status/llm_calls/token_usage / error 刷新+直播
chat_message role, content 运行内问答广播

Chat SSE(POST /api/chat,fetch 手解析):intent(意图+缺失槽位)、status(深搜进行中/降级)、delta(增量文本)、run_created/run_link(运行卡片/跳转)、error、done。

2.6 会话与意图路由详细设计(routers/chat.py)

会话模型:Conversation(标题自动 = 首条消息前 24 字;绑定 run 后改为「模块+专利号」)↔ Run 一对一可选;ChatMessage 双外键(conversation_id 为主,run_id 兼容);Upload 文本抽取入库(见 §2.7)。

消息处理主干(详见流程图 §3.4):

  1. 加载会话与绑定 Run;绑定 Run → 系统提示注入 _blackboard_digest(专利/要素/文献/覆盖/组合/报告摘要)成为「运行内引导对话」;未绑定 → 走意图路由。
  2. 意图分支各自持久化 assistant 回复并 return;user 消息统一在路由后落库。
  3. 常规问答:命中时效关键词(最新/新闻/2025/2026/官网/价格…,运行内追加 搜索/查一下/联网)且 use_deep_search → DeepSearchAgent(1 轮 5 条)→ 摘要以 system 消息注入 → glm_client.chat_stream 增量输出。
  4. 上传兜底:create_analysis 且无专利号/交底书 → 最新上传文本(>80 字)即交底书。

文件上传(conversations.py):15MB 上限;txt/md/csv/json/xml/html 直接 UTF-8;PDF pypdf 前 120 页;DOCX zip 解 word/document.xml 正则剥标签;兜底可打印率 >0.85。会话已绑定 Run 时:抽取文本(≤9000 字)直接追加为 user_added 文献入比对池 + 发 reference_added。

2.7 数据模型与迁移(models.py / db.py)

六表:runs(blackboard JSON 列为核心)、agent_steps(run_id+created_at 索引,步骤回放)、conversations、uploads、chat_messages(conversation_id/run_id 双索引)、findings(run_id+created_at 索引,score 排序输出)。全部 32 位 hex UUID 主键、UTC 时间戳、级联删除。

轻量迁移 _migrate(无 Alembic):PRAGMA 检列补 chat_messages.conversation_id、runs/conversations.user_id(回填 demo-user + 建索引);旧 run 消息按 run_id 归并成会话,全局散消息归并为「历史对话」。

Finding(CitableFinding)落库规则:逐要素×覆盖映射 → s103(有组合时)或 s102,score=verify pass?1.0:0.6;硬缺口 → gap score 0.0 low;全覆盖组合 → s103 score 0.8,citations 列出主+次全部文献;侵权侧 evidence_of_use/gap。

2.8 企业级横切详细设计

2.8.1 弹性(resilience.py)

stateDiagram-v2
    [*] --> closed: 初始
    closed --> open: 连续失败≥5 或 half_open 再失败
    open --> half_open: 距 opened_at ≥ reset_seconds(30s)
    half_open --> closed: 探测成功(计数清零)
    half_open --> open: 探测失败

resilient_call(name, fn→(result, ok)):先 breaker.allow()(open 直接抛 CircuitOpenError);尝试 ≤3 次,ok=False 视为提供方级失败(计熔断+重试);异常经 default_retriable(Timeout/Transport/408/425/429/5xx)判定,不可重试立即抛;退避 0.8×2^(n-1) + U(0,0.3) 秒。按依赖独立熔断器:glm / glm_websearch / zhihuiya。

2.8.2 其余横切

模块 设计
metrics.py 自实现 Counter/Summary(Prometheus 文本格式):lawsight_requests_total{method,path,status}、lawsight_request_latency_ms、lawsight_llm_calls_total{purpose,status}、lawsight_zhihuiya_calls_total{endpoint,status}、lawsight_websearch_calls_total、lawsight_runs_total、tokens in/out
middleware.py RequestContext:X-Request-Id(或生成 16 位)+ X-User-Id(清洗 [^A-Za-z0-9_-])注入 contextvars;未捕获异常→500+request_id;响应头回写请求 ID。RateLimit:令牌桶 per-IP,RPM 可配,429 + X-RateLimit-Remaining
token_metrics.py record(usage):内存按 matter(=run_id/conversation)聚合 + Prometheus 计数 + Redis Stream XADD wf:llm_tokens MAXLEN≈100000(9 字段 ts/user_id/matter_id/stage/name/model/tokens_in/out/total);Redis 不可用一次性标记降级、仅日志告警不阻塞
logging_config.py JSON 行格式:ts/level/logger/msg/request_id + extras(agent/run_id/step/duration_ms/component/status/detail);console+file 双通道;httpx/httpcore/uvicorn.access 压 WARNING

2.9 前端详细设计(frontend/src)

组件树:

App.vue → ShellView(唯一视图, 路由 / 与 /run/:id)
├─ ChatPanel           消息流(user/assistant/event/upload/runcard 五种消息形态)
│                      会话抽屉(列表/新建/切换/删除) 📎上传 深搜开关 发送/停止键
├─ WelcomePanel        无运行时欢迎页+最近运行
├─ 工作台(activeRunId 存在时)
│  ├─ 头部:面包屑(PN+状态点) 进度条 三级页签 Secure/Privileged 徽章 停止/恢复按钮
│  ├─ OverviewPanel     进度/统计/token 用量
│  ├─ Evidence & Chart 子页签
│  │  ├─ PriorArtPanel        六列证据表:PN/标题/受让人/日期/类型/Why relevant + C1..Cn 覆盖条 + 附图缩略
│  │  └─ ClaimChartMatrix     矩阵:行=Limitation(按 CLAIM 分组) 列=文献;三色(Strong绿/Partial琥珀/Gap灰)
│  │                          过滤(关键词/日期基准/来源/强度) + Broad|Proposed|Narrow 档位切换(→steer)
│  └─ FindingsPanel     report 摘要/结论/风险/行动 + 组合论证 + §102 候选

状态管理:无 Pinia,ShellView 本地 ref 集中持有 runs/sessions/activeConversation/activeRunId/run/bb/progress/interp,props 下发 + emit 上抛。数据新鲜度 = SSE 事件触发 refresh() + 6s setInterval 兜底;EventSource 自动重连,from_index 支持断线续播。

双 SSE 消费:运行事件用 EventSource(subscribeRunEvents 显式注册 15 种事件);对话用 fetch reader 手解析帧(streamChat,POST 体无法用 EventSource)。

2.10 配置项清单(config.py,.env 覆盖)

组 键(默认)
GLM glm_api_key / glm_base_url / glm_model(glm-5.3-flashx) / glm_timeout(120) / glm_search_engine(search_std) / websearch_timeout(30)
智慧芽 zhihuiya_api_key / zhihuiya_base_url / zhihuiya_timeout(45)
编排预算 max_independent_claims(2) max_elements(8) max_gap_rounds(1) max_combination_refs(3) llm_max_calls_per_run(60)——均可被 Run.params 逐运行覆盖
弹性/限流 breaker_failure_threshold(5) breaker_reset_seconds(30) retry_max_attempts(3) retry_backoff_base(0.8) rate_limit_rpm(600)
基础设施 database_url(sqlite+aiosqlite) redis_url redis_stream(wf:llm_tokens) redis_stream_max(100000) cors_origins static_frontend_dir
观测 log_level(INFO) log_file(logs/app.log)

第三部分 流程图汇总

3.1 端到端时序:对话发起分析 → 工作台实时展示

sequenceDiagram
    autonumber
    actor U as 用户
    participant CP as ChatPanel
    participant CHAT as POST /api/chat
    participant IA as IntentAgent
    participant GLM as GLM(智谱)
    participant ORC as Orchestrator(后台task)
    participant ZHY as 智慧芽
    participant BUS as EventBus
    participant SV as ShellView/工作台
    participant DB as SQLite

    U->>CP: "分析 US12003836B2 的无效性"
    CP->>CHAT: SSE 请求
    CHAT->>IA: history + message
    IA->>GLM: 意图识别+槽位抽取(llm_json)
    GLM-->>IA: intent=create_analysis 槽位齐
    CHAT->>DB: 建 Run(created)
    CHAT->>ORC: asyncio.create_task(execute)
    CHAT-->>CP: event: run_created(运行卡片)
    CP->>SV: openRun(run_id) → 路由 /run/:id
    SV->>BUS: EventSource /runs/:id/events

    Note over ORC: 黑板流水线(见 §3.2)
    ORC->>ZHY: P069→P018 加载专利/权利要求
    ORC->>GLM: ClaimParser 等各智能体调用
    ORC->>DB: _checkpoint() 逐文献落库
    ORC->>BUS: progress/agent_step/reference_added/coverage_updated…
    BUS-->>SV: SSE 事件
    SV-->>CP: pushAgentEvent(智能体直播)
    SV->>SV: refresh() 拉取 blackboard 更新面板

    ORC->>DB: 终态 awaiting_review + findings 落库
    ORC->>BUS: run_completed(status, llm_calls, token_usage)
    SV->>SV: 刷新工作台(Claim Chart/Findings 就绪)
    U->>SV: 律师引导: 换解释档/聚焦/补文献(steer)

3.2 无效性分析主流程(run_invalidity)

flowchart TD
    S(["开始 run_invalidity"]) --> RES{"黑板已有 elements?<br/>(检查点恢复)"}
    RES -->|"是"| SKIP["跳过解析<br/>进度20% 恢复模式"]
    RES -->|"否"| LP["load_patent: P069解析pn→P018权利要求<br/>(失败降级P012题录; 无pn=交底书模式)"]
    LP --> CP["ClaimParser: 要素拆解+三档解释<br/>+key_terms/function_summary (空结果重试1次)"]

    SKIP --> ROUND["gap轮循环 round=1..1+max_gap_rounds"]
    CP --> ROUND
    ROUND --> SS["SearchStrategist: 每要素三视角查询<br/>(缺口轮仅gaps且换表述)"]
    SS --> SRC["双通道检索: 每要素<br/>P008语义(建议解释,pbd_to截止,limit4)<br/>+ P002检索式(每查询limit3)"]
    SRC --> DEDUP["pn去重 + 剔除本专利"]
    DEDUP --> EH["EvidenceHunter: P018批量取权要求<br/>→P012批量补题录→仍无文本丢弃"]
    EH --> MAPLOOP["映射循环: todo_refs = 全部文献 - mapped_ref_ids"]
    MAPLOOP --> EM["ElementMapper: 每文献1次LLM覆盖全部要素<br/>三态判定+逐字quote (异常仅记log)"]
    EM --> CKP["_checkpoint() 落库"]
    CKP --> MAPLOOP
    MAPLOOP -->|"全部完成"| CV["CitationVerifier: 逐字核验<br/>fail的met→uncertain (§3.5)"]
    CV --> WHY["_backfill_why_relevant 回填证据表说明"]
    WHY --> IR["InvalidityReasoner:<br/>coverage矩阵+gaps识别<br/>+§102单篇全查 +§103贪心集合覆盖组合<br/>+LLM写motivation(Graham/KSR)"]
    IR --> GAP{"仍有 gaps 且<br/>未到轮次上限?"}
    GAP -->|"是"| ROUND
    GAP -->|"否"| RG["ReportGenerator: 执行摘要/结论/风险/行动"]
    RG --> FD["_build_findings: s102/s103/gap 落 Finding 表"]
    FD --> DONE(["进度99% → awaiting_review"])

    STOP["steer stop → ctx.stop_requested"] -.打断循环.-> MAPLOOP
    STOP -.打断.-> SRC

3.3 侵权分析主流程(run_infringement)

flowchart TD
    S(["开始 run_infringement"]) --> LP["load_patent: P069→P018(无pn则交底书伪claim1)"]
    LP --> CP["ClaimParser 要素拆解+三档解释 (20%)"]
    CP --> PD["ProductDiscovery (35%):<br/>深搜候选公司产品(1轮5条)→LLM结构化leads≤6<br/>(失败→检索结果直出低置信线索)"]
    PD --> Q["取前4条线索构造EOU查询:<br/>公司+产品+首要素key_terms前3"]
    Q --> WEB["EvidenceHunter.run_web (55%):<br/>每查询GLM web_search 4条→URL去重入库"]
    WEB --> MAP["映射循环(前8条web文献):<br/>ElementMapper逐证据比对→CitationVerifier核验"]
    MAP --> GAPA["缺口分析: verdict=met的要素为覆盖<br/>其余=决定性缺口(gaps) → coverage_updated"]
    GAPA --> RG["ReportGenerator.run_infringement:<br/>claim chart 满足度摘要+建议行动"]
    RG --> FD["_build_findings: evidence_of_use / gap"]
    FD --> DONE(["进度99% → awaiting_review"])

3.4 Chat 意图路由流程(routers/chat.py)

flowchart TD
    IN(["POST /api/chat message"]) --> BIND{"会话已绑定 Run?"}
    BIND -->|"是"| DIGEST["系统提示注入黑板摘要<br/>→ 运行内引导对话"]
    BIND -->|"否"| IA["IntentAgent(近8轮历史+当前消息)<br/>失败→关键词规则兜底"]
    IA --> UP{"create_analysis 且<br/>无pn无交底书?"}
    UP -->|"有最新上传>80字"| FILL["上传文本=交底书<br/>移除patent_input缺失"]
    UP -->|"否"| INT
    FILL --> INT{"intent?"}

    INT -->|"create_analysis"| MS{"missing_slots?"}
    MS -->|"空(齐)"| CREATE["建Run + create_task启动编排器<br/>绑定会话(标题=模块+PN)<br/>SSE: run_created → 对话卡片直达工作台"]
    MS -->|"module/patent_input"| ASK["追问一项(missing_slots)<br/>SSE: delta → done"]
    INT -->|"resume_run"| RES{"找到 stopped/failed 且<br/>有elements检查点<br/>且无活跃ctx?"}
    RES -->|"是"| RSM["Orchestrator续跑 + 绑定会话<br/>SSE: run_created + 检查点摘要回复"]
    RES -->|"否"| RNO["回复无可恢复分析"]
    INT -->|"view_run"| VIEW["按pn/最近定位Run → 绑定会话<br/>SSE: run_link → 跳工作台"]
    INT -->|"general_qa/help"| QA

    QA["常规问答: 命中时效关键词且use_deep_search?<br/>→DeepSearchAgent(1轮)→摘要注入system消息"]
    QA --> STREAM["glm_client.chat_stream 增量输出<br/>SSE: delta… → done; 落库assistant消息"]
    DIGEST --> QA
    CREATE --> PERSIST["user消息统一落库"]
    ASK --> PERSIST
    RSM --> PERSIST
    RNO --> PERSIST
    VIEW --> PERSIST

3.5 引用核验与降级流程(CitationVerifier.verify)

flowchart TD
    A(["verify(quote, ref.text)"]) --> B{"quote 非空?"}
    B -->|"否"| FAIL(["fail: empty quote"])
    B -->|"是"| N["归一化: 空白折叠→弯引号/中文标点转ASCII→casefold"]
    N --> C{"归一化quote ⊆ 归一化原文?"}
    C -->|"是"| PASS(["pass"])
    C -->|"否"| F["模糊匹配: quote前缀窗口≤120字符<br/>与原文最长公共匹配 / 窗口长"]
    F --> G{"比值 ≥ 0.97?"}
    G -->|"是"| WARN(["warn: 轻微空白/标点漂移"])
    G -->|"否"| FAIL2(["fail: quote not found (匹配度%)"])

    PASS & WARN & FAIL2 --> H["写回 mapping.verify / verify_reason"]
    H --> I{"verdict=met 且 verify=fail?"}
    I -->|"是"| J["降级: met→uncertain<br/>rationale前缀'[引用核验未通过]'<br/>步骥标记degraded"]
    I -->|"否"| K["保持"]
    J & K --> L["下游: 仅 met且verify∈{pass,warn} 计入coverage<br/>Finding.verified = verify∈{pass,warn}<br/>score: pass=1.0 / warn=0.6"]

3.6 检查点恢复流程

flowchart TD
    TRG(["触发: 工作台恢复按钮 / POST /resume / 对话说'继续分析'"]) --> V1{"Run 存在?<br/>user_id 匹配?"}
    V1 -->|"否"| E404["404/403"]
    V1 -->|"是"| V2{"status?"}
    V2 -->|"awaiting_review"| E409a["409 已完成,直接引导"]
    V2 -->|"running 或活跃ctx"| E409b["409 正在运行"]
    V2 -->|"stopped/failed"| V3{"黑板有 elements?"}
    V3 -->|"否"| E422["422 无检查点保护<br/>(解析未完成,不可恢复)"]
    V3 -->|"是"| GO["status=running + 发run_resumed事件<br/>+ 后台启动Orchestrator"]

    GO --> R1["恢复语义①: 跳过ClaimParser<br/>(直接用黑板elements,进度20%)"]
    R1 --> R2["恢复语义②: 检索去重<br/>seen_pn=已有文献pn集合"]
    R2 --> R3["恢复语义③: 映射差集<br/>todo_refs=全部文献-mapped_ref_ids"]
    R3 --> R4["续跑: 检索→抓取→未完成映射→核验→推理→报告"]

3.7 前端 SSE 事件处理流(ShellView.attachSSE)

flowchart TD
    A(["EventSource /runs/:id/events?from_index=N"]) --> B{"event.type"}
    B -->|"agent_step 且 status≠ok"| C["ChatPanel 直播行: agent action status"]
    B -->|"log"| D["ChatPanel 直播行: message<br/>(每次LLM调用/检索失败可见)"]
    B -->|"elements_ready"| E["直播'已解析N个要素' + refresh()"]
    B -->|"reference_added"| F["直播'新增文献PN' + refresh()"]
    B -->|"coverage_updated"| G["直播'覆盖X% 缺口N个' + refresh()"]
    B -->|"progress"| H{"stage 变化?"}
    H -->|"是"| I["直播'阶段 PCT% · detail' + 更新进度条"]
    H -->|"否"| J["仅更新进度条"]
    I & J --> K{"pct ≥ 99?"}
    K -->|"是"| L["refresh()"]
    B -->|"run_completed / run_failed"| M["直播终态 + refresh()<br/>(拉取最终黑板与findings)"]
    B -->|"其他(patent_loaded/…) "| N["忽略"]

    C & D & E & F & G & L & M --> O["refresh(): 并行拉 getRun + blackboard<br/>→ 驱动 Overview/PriorArt/ClaimChart/Findings"]
    P["6s setInterval 兜底轮询"] --> O
    Q["EventSource onerror 自动重连<br/>from_index 断线续播"] --> A

附:可靠性设计速查

4. 系统概述

定位:用户在左侧对话中用自然语言发起分析(如"分析 US12003836B2 的无效性"),系统自动完成意图识别 → 槽位抽取 → 创建并启动多智能体分析流水线;右侧工作台实时展示证据表、Claim Chart 矩阵与可引用发现(Findings)。

核心设计理念(三条主线):

理念 落地方式
黑板状态机 一次分析(Run)的全部中间产物(要素/文献/映射/覆盖/组合/发现)收敛在一个 JSON 黑板中,既是智能体间的通信介质,也是检查点与持久化投影
两阶段人机协同 阶段一自主运行至 awaiting_review;阶段二律师通过 steer(换解释档/聚焦要素/补文献/停止)引导定向重跑
反幻觉硬约束 LLM 摘录的引用必须能在文献原文中逐字核验(归一化包含匹配 + 前缀模糊匹配),核验失败自动降级 met → uncertain 并标注原因

技术栈:FastAPI + SQLAlchemy(async) + httpx(后端);Vue3 + Vite + Element Plus + ECharts(前端);GLM glm-5.3-flashx(推理模型)、GLM web_search(联网)、智慧芽开放平台(专利数据)。


4. 核心机制

4.1 黑板(Blackboard)

Run.blackboard(JSON 列)是系统的中枢数据结构:

patent{pn,title,claims}  elements[](要素:text + 三档解释 + key_terms + function_summary + is_112f)
queries[](三视角检索计划)  references[](文献:patent/web/user_added,含全文 text)
mappings[](要素×文献:verdict 三态 + quote + locus)  coverage{element_id→[met 映射]}
gaps[](硬缺口要素)  combinations[](§103 组合 + motivation 论证)  s102_ref
product_leads[](侵权侧线索)  findings[]  report{}  progress{pct,stage}  token_usage  law_domain
  • 智能体只读写黑板,彼此不直接调用 → 流水线步骤可插拔、可恢复。
  • _checkpoint() 在关键节点(逐文献映射后、进度更新时)整体落库,配合 mapped_ref_ids 跳过已完成文献 → 停止/失败后可从检查点续跑(POST /runs/{id}/resume 或对话说"继续分析")。

4.2 事件总线与实时性

  • EventBus(进程内):emit(run_id, type, **data) → 订阅队列 + 每 run 最多 2000 条历史(支持 SSE 断线 from_index 回放)。
  • 每个可观察状态迁移都是具名事件:run_started / agent_step / progress / patent_loaded / elements_ready / reference_added / coverage_updated / combinations_ready / citations_verified / steering_applied / run_completed / run_failed 等。
  • SSE 两条通道:GET /runs/{id}/events(运行事件,15s 心跳保活)与 POST /chat(对话增量 delta/intent/run_created/run_link)。前端 EventSource 自动重连 + 6s 轮询兜底。

4.3 Chat-first 意图路由(routers/chat.py)

会话未绑定 Run 时,每条消息先过 IntentAgent:

意图 动作
create_analysis(槽位齐) 建 Run + 后台启动 → SSE run_created → 前端卡片直达工作台,会话绑定 Run
create_analysis(缺槽位) 对话追问(一次只问一项),槽位跨轮累积
resume_run 定位可恢复 Run → 检查点续跑
view_run 按专利号/最近定位 Run → run_link 跳工作台
其他 常规问答(Run 内注入黑板摘要;命中时效关键词走 DeepSearchAgent 联网)

鲁棒性:LLM 意图失败降级为关键词规则(模块词/视图词/恢复词表 + 专利号正则 _PN_RE 兜底);上传文件(txt/md/pdf/docx 解析文本)可作为交底书替代专利号;会话绑定 Run 后上传自动入对照文献池。


5. 智能体流水线

所有智能体继承 BaseAgent:llm_json/llm_text 强制走预算护栏(llm_max_calls_per_run,默认 60 次)、每次调用发 log 事件、系统提示统一前缀注入法域块(US §102/§103+Phillips / CN 专利法 22 条三步法 / EP Art.54/56,law_domains.py)。

5.1 无效性(invalidity)

load_patent(P069 解析 pn → P018 权利要求,失败降级 P012 题录)
→ ClaimParser      权利要求 → 要素拆解 + 三档解释(broad/proposed/narrow)+ key_terms/function_summary;空结果自动强指令重试
→ [gap 轮循环 ≤ 1+max_gap_rounds]
   → SearchStrategist   每要素三视角查询(terms/functional/problem_solution),缺口轮换表述
   → 双通道检索          P008 语义(要素建议解释为查询)+ P002 检索式;按 pn 去重、剔除本专利
   → EvidenceHunter     P018/P012 批量抓取全文(2 次往返替代 N×2),批量失败降级逐条
   → ElementMapper      每文献一次 LLM 调用覆盖全部要素(保全文上下文),三态判定 + 逐字 quote;缺失要素补 not_met
   → CitationVerifier   逐字核验:归一化(空白/中英标点/casefold)包含匹配;前缀 97% 模糊 → warn;fail 则 met→uncertain
   → InvalidityReasoner 覆盖矩阵 + 缺口识别 + §102 单篇全查;§103 组合 = 带束界的贪心集合覆盖(score = 覆盖数 − 0.15×文献数)
                        最优组合再让 LLM 写 motivation to combine(Graham/KSR,含 teaching-away 弱点)
→ ReportGenerator  执行摘要/结论/风险/建议行动(仅可引用给定材料)
→ _build_findings  逐要素发现落库(s102/s103/gap,带 citations、score、verified)

5.2 侵权(infringement)

load_patent → ClaimParser
→ ProductDiscovery   深搜发现候选公司/产品线索(LLM 结构化,失败降级检索结果直出)
→ EvidenceHunter.run_web   目标公司×产品×要素关键词 → 网页证据(EOU)
→ ElementMapper + CitationVerifier(同上,对 web 证据)
→ 缺口分析(verdict=met 的要素为覆盖,其余为决定性缺口)
→ ReportGenerator(run_infringement)+ Findings(evidence_of_use / gap)

5.3 DeepSearchAgent(通用工具)

plan → search → reflect 循环(第 2 轮针对缺口补查、避免重复查询),供侵权产品发现、公开出版物的补充现有技术与对话联网问答使用。

6. 工程质量设计(enterprise/)

关注点 实现
弹性 resilient_call:指数退避 + 抖动重试(默认 3 次);按依赖熔断器(closed→open→half_open,5 次失败/30s 复位),重试判定 default_retriable(超时/传输/408/425/429/5xx)
限流 令牌桶中间件(按客户端 IP,RPM 可配),X-RateLimit-Remaining 响应头
观测 JSON 结构化日志 + contextvars(request_id/user_id/matter_id/stage 全链路透传);Prometheus 文本指标(HTTP/LLM/智慧芽/检索调用、延迟);token 用量按 matter 聚合 + Redis Stream(wf:llm_tokens,不可用降级不阻塞)
推理模型适配 glm-5.3-flashx 常开思考、思考共享 max_tokens:空内容或 finish_reason=length → 2 倍预算重试;JSON 解析失败 → 4 倍再试;parse_json_block 容错链(围栏提取 → 平衡括号截取 → 内层引号重转义 → 截断修复裁切)
检索适配 search_std 对长查询 AND 全词易 0 结果 → 自动降级查询(去引号取前 4 词 → 前 2 词)
预算护栏 每 run LLM 调用上限(默认 60);要素/独权/组合文献/缺口轮数均可 params 覆盖

7. 关键设计决策与权衡

  1. 黑板而非消息传递:智能体间解耦、天然可检查点、JSON 单列即可持久化(SQL 无 schema 演进负担);代价是黑板结构靠 setdefault 约定维护,无强 schema 校验。
  2. 每文献一次映射调用(而非每要素):模型看到文献全文上下文,判定更准、调用数 = 文献数;配合逐字核验兜住幻觉。
  3. 集合覆盖用确定性算法(贪心+束界),动机论证用 LLM:组合搜索可复现可测试,法律修辞交给模型——各用所长。
  4. 降级链贯穿始终:意图→规则、批量→逐条、检索长查询→短查询、met→uncertain、Redis→日志。外部依赖故障表现为快速失败 + 明确错误,不阻塞服务。
  5. 进程内 EventBus + 单实例假设:简单可靠,但水平扩展需换 Redis Pub/Sub;运行态 ctx(steering inbox)也是内存态,重启丢失(靠黑板检查点补偿)。
  6. 单模型多角色:所有智能体共用 GLM 一个模型,靠系统提示(+法域块)分化角色;未做模型路由/分级。
最近更新: 2026年11月4日 13:49:28
浏览: 15

[[total]] 条评论

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