1. 系统概览
| 项 | 内容 |
|---|---|
| 定位 | FTO(Freedom to Operate)报告生成 Demo:七阶段多智能体工作流 + 对话式人机协同 |
| 后端 | Python 3.12 · FastAPI · asyncio(每任务一协程) · Pydantic 数据契约 |
| 存储 | SQL(WAL,5 张表:任务/事件/LLM流水/对话/轨迹)+ Redis Stream(wf:llm_tokens 令牌消息队列) |
| 模型 | GLM 文本(glm-5.3-flashx,含 glm.chat 调用面)+ GLM 视觉(glm-4.5v,调用面 glm.vision) |
| 外部服务 | 智慧芽开放平台(P002 检索 / P011 著录 / P010 图传 / P060 以图搜图)+ Google Patents 权利要求与外观视图抓取 |
| 前端 | Vue3 + Vite:任务列表/创建 · Chatbot 对话台 · 工作台(阶段/判定/报告/Token)· Redis Stream 实时视图 |
| 双模式 | invention 发明专利 FTO(claim chart 全要素规则)/ design 外观设计 FTO(组件暴力拆解 × 视觉比对) |
| 容错 | MOCK_LLM / MOCK_SEARCH 离线开关;单语检索失败隔离;LLM 失败规则兜底;Redis 失联降级 SQLite |
2. 总体架构图
graph TB
subgraph FE["前端 web/ (Vue3 + Vite)"]
APP["App.vue<br/>任务列表/创建表单<br/>多图上传·专利文件上传·中英切换"]
CHAT["ChatPanel.vue<br/>对话控制台(关卡卡片)"]
DETAIL["TaskDetail.vue + VisualPanel.vue<br/>阶段进度/漏斗/claim chart/风险矩阵/报告"]
TS["TokenStream.vue<br/>Redis Stream 实时指标"]
end
subgraph API["API 层 server/app"]
MAIN["main.py<br/>FastAPI 入口·CORS·静态文件"]
APIR["api.py<br/>REST + SSE·全部需 user_id"]
end
subgraph CORE["编排与智能体核心"]
WF["workflow.py ★<br/>WorkflowEngine(7阶段流水线)<br/>检查点暂停/恢复·3关卡·rerun"]
DLG["dialog.py<br/>Dialog Orchestrator<br/>21个工具注册表·两段式执行"]
HAR["harness.py<br/>Agent Harness<br/>输入/输出守卫·步数/token预算·轨迹"]
SCH["schemas.py<br/>Pydantic 数据契约(FTOState...)"]
end
subgraph INFRA["基础设施"]
LLM["llm.py<br/>GLM 客户端 chat/chat_json/vision<br/>JSON鲁棒解析·token计量挂点"]
ZHY["zhihuiya.py<br/>智慧芽客户端 P002/P011/P010/P060<br/>Google Patents 抓取"]
TM["token_metrics.py<br/>SQLite流水 + XADD wf:llm_tokens"]
STORE["store.py<br/>SQLite 5表持久层"]
IMG["images.py<br/>上传/降采样/base64/PIL标注图"]
DOCS["docs.py<br/>PDF/DOCX/TXT/MD抽取·专利号解析"]
end
subgraph EXT["外部服务"]
GLM[("智谱 GLM<br/>glm-5.3-flashx / glm-4.5v")]
ZHYA[("智慧芽开放平台<br/>connect.zhihuiya.com")]
GP[("Google Patents")]
REDIS[("Redis<br/>Stream wf:llm_tokens")]
SQLITE[("SQLite<br/>data/fto.db (WAL)")]
end
APP --> APIR
CHAT -->|"POST /tasks/{id}/chat"| APIR
DETAIL -->|"GET /workflow · SSE /events/stream"| APIR
TS -->|"GET /tokens/stream"| APIR
MAIN --- APIR
APIR --> WF
APIR --> DLG
APIR --> STORE
APIR --> IMG
APIR --> DOCS
WF --> SCH
WF --> LLM
WF --> ZHY
WF --> STORE
DLG --> HAR
DLG --> LLM
DLG --> STORE
DLG --> ZHY
LLM --> TM
TM --> SQLITE
TM -->|"XADD 9字段契约"| REDIS
STORE --> SQLITE
LLM --> GLM
ZHY --> ZHYA
ZHY --> GP
调用关系要点
- 每次任务 =
WorkflowEngine中的一个asyncio.Task协程(engine.launch()幂等启动); - 每次对话 = 一个 Harness Run(
H-xxxx),完整轨迹落库可回放; - 每次 LLM 调用(含对话与视觉)都经
token_metrics.record()双写 SQLite + Redis Stream。
3. 七阶段工作流流程图(含关卡与检查点)
flowchart TD
START(["POST /tasks 创建<br/>auto_start=true"]) --> S1
S1["S1 FeatureParser 特征解析<br/>──────────<br/>invention: 技术特征清单 F-xx·must/should/optional<br/>design: 设计要点 D-xx·形状/图案/色彩<br/>中英双语检索关键词"]
S1 --> CK1{{"检查点落盘<br/>FTOState 快照"}}
CK1 --> G1{"关卡①特征确认<br/>gates_enabled?"}
G1 -->|"awaiting_review<br/>对话卡片推送"| H1["人工: 批准/改特征/退回"]
H1 -->|approve| S2
G1 -->|"关闭或已确认"| S2
S2["S2 PatentRetriever 专利检索<br/>──────────<br/>①中文TACD + 英文TACD 分路检索(单语失败不隔离影响)<br/>②零命中→LLM泛化产品类别词重试<br/>③design模式: P010上传主视图→P060以图搜图(DESIGN优先)<br/>④P011批量著录补全·同PN去重"]
S2 --> CK2{{"检查点 s2_search"}} --> S3
S3["S3 LegalFilter 法律状态过滤<br/>──────────<br/>①地域过滤(目标法域+WO,全滤则回退)<br/>②patent_type 法律状态代理<br/>③LLM相关性分档 high/med/low(失败回退全medium)<br/>④design: DESIGN类优先 ⑤上传专利文件直入比对"]
S3 --> CK3{{"检查点 s3_filter"}} --> S4
S4["S4 ClaimAnalyzer 权利要求比对<br/>──────────<br/>逐专利扇出·每篇完成即子检查点<br/>━━ invention ━━<br/>权利要求→MRE最小要素·逐要素映射<br/>verdict: literal/equivalent/not_covered/uncertain<br/>全要素规则→claim chart<br/>━━ design ━━<br/>A: 视觉暴力拆解(6-12组件,bbox/九宫格)<br/>B: 逐组件对比→整体视觉效果综合判断"]
S4 -->|每篇专利| SUBCK{{"子检查点<br/>checkpoint=s3_filter<br/>篇间可暂停"}}
SUBCK --> S4
S4 --> CK4{{"检查点 s4_mapping"}} --> S5
S5["S5 RiskAssessor 风险评估<br/>──────────<br/>三维评级: 侵权可能性/专利有效性/执法倾向<br/>→综合 High/Medium/Low<br/>LLM失败→规则引擎兜底(claim chart统计)"]
S5 --> CK5{{"检查点 s5_risk"}}
CK5 --> G2{"存在 High<br/>且未复核?"}
G2 -->|"是"| H2["关卡②律师复核<br/>claim chart+评分依据卡片<br/>确认/改判(override_risk)/定向重跑"]
H2 -->|approve| S6
G2 -->|"否"| S6
S6["S6 ReportWriter 报告撰写<br/>──────────<br/>总体判定=规则计算(杜绝数字幻觉)<br/>LLM生成中英双语执行摘要<br/>模板渲染 Markdown(双模板:发明/外观)"]
S6 --> CK6{{"检查点 s6_report"}} --> S7
S7["S7 QAReviewer 质量审核<br/>──────────<br/>①规则校验(专利遗漏/空报告/评级孤儿)<br/>②LLM交叉一致性(数字/结论/术语)<br/>③发现issue→修订循环(≤qa_max_retry)"]
S7 --> CK7{{"检查点 s7_qa"}}
CK7 --> G3{"关卡③终审签发"}
G3 -->|approve| DONE(["completed<br/>报告附签发人+时间戳"])
G3 -->|"reject 批注"| BACK["checkpoint=s5_risk<br/>批注入 report_edit_notes"]
BACK --> S6
PAUSE(["POST /pause"]) -.->|"pause_requested=1<br/>下一检查点生效"| S4
检查点语义(workflow.py:115-123,261-263)
- 阶段完成 →
_save(state, checkpoint=stage)持久化FTOStateJSON 快照; - S4 内每篇专利完成即子检查点(checkpoint 保持
s3_filter,恢复后跳过pn in analyses的专利); - 暂停 = 置
pause_requested标志,在阶段边界或 S4 篇间生效(_PausedMidway异常自然终止协程); - 恢复 =
STAGES[idx(checkpoint)+1:]续跑,不重放已完成工作; - 重跑 =
POST /rerun:full清空全部从 S1 /search保留特征从 S2,run_count轮次留痕。
4. 任务生命周期状态机
stateDiagram-v2
[*] --> created: POST /tasks
created --> running: start / auto_start
running --> running: 各阶段执行(S1→S7)
running --> paused: 暂停请求@检查点
running --> awaiting_review: 阶段后开关卡(S1后/S5后High/S7后)
running --> failed: 阶段异常(error留痕)
paused --> running: resume(从检查点)
failed --> running: start(从检查点)
awaiting_review --> paused: 关卡①②approve→engine.launch
paused --> running: 隐式
awaiting_review --> completed: 关卡③approve(签发)
awaiting_review --> paused: 关卡③reject→checkpoint=s5_risk重跑S6/S7
paused --> cancelled: cancel
running --> cancelled: cancel
completed --> [*]
cancelled --> [*]
note right of awaiting_review
gate ∈ {gate_feature, gate_legal, gate_final}
超时: review_timeout_hours=72h(提醒)
对话 approve_gate 与 REST /review 共用 resolve_gate()
end note
5. 对话式人机协同与 Agent Harness(时序图)
sequenceDiagram
autonumber
participant U as 用户(ChatPanel)
participant A as api.py POST /chat
participant D as dialog.py 协调者
participant H as harness.py 运行框架
participant L as llm.py GLM
participant T as 工具(21个)
participant S as store.py SQLite
U->>A: {user_id, message, lang}
A->>D: handle_message()
D->>S: 落库用户消息
D->>H: 创建 HarnessRun(H-xxxx)
H->>H: 输入守卫: 提示注入模式拦截→guarded终止
loop ≤3步 & ≤60000 token
D->>L: chat_json(上下文+21工具清单+历史)
L-->>H: llm_decision 步骤(延迟/token留痕)
alt tool=null
L-->>D: 直接回复(闲聊/领域知识)
else 选择工具
D->>T: execute(查询/修改/干预/裁决)
T->>S: 状态修改落盘+事件审计
T-->>H: tool 步骤(结果摘要留痕)
Note over T: 终止工具: approve_gate/<br/>reject_gate/cancel_task
end
end
D->>L: 基于工具链结果生成自然语言回复
H->>H: 输出守卫: 回复中专利号∉任务范围→疑似幻觉标注
D->>S: 回复落库(含工具结果载荷)
D->>S: harness_runs 轨迹落库(可回放)
D-->>U: {reply, tool_chain, harness, task状态}
工具注册表(21 个,dialog.py:62-104)
| 类别 | 工具 |
|---|---|
| 查询(多源) | get_task_overview get_features get_patents get_claim_chart get_risk get_report get_token_stats get_search_strategies live_search(智慧芽实时) |
| 修改(干预) | update_feature add_feature delete_feature record_clarification |
| 流程干预 | rerun_patent(定向单篇) rerun_task override_risk(律师改判回流) request_report_edit pause_task resume_task cancel_task |
| 关卡裁决 | approve_gate reject_gate |
治理机制:事实必须经工具获取(系统提示硬约束);守卫拦截状态 guarded;预算耗尽优雅收敛 capped;每步 llm_decision/tool 含参数、结果摘要、延迟、token 全留痕。
6. S4 双模式比对设计图(核心判定引擎)
flowchart TB
subgraph INV["invention 发明专利 FTO"]
I1["权利要求文本<br/>Google Patents抓取(claims_source=google)<br/>失败回退摘要(abstract)·上传文件(upload)"] --> I2
I2["LLM拆解3-6个MRE最小要素"] --> I3["逐要素映射产品特征<br/>literal字面/equivalent等同/<br/>not_covered未覆盖/uncertain"]
I3 --> I4{"全要素规则<br/>(chart回推兜底)"}
I4 -->|全部literal或equivalent| I5["infringement"]
I4 -->|存在not_covered| I6["non_infringement"]
I4 -->|其余| I7["uncertain"]
I5 & I6 & I7 --> I8["claim chart + 规避设计建议"]
end
subgraph DES["design 外观设计 FTO"]
D1["产品多图(≤10,本地base64降采样)<br/>+ 专利视图(Google抓取→本地化)"] --> D2
D2["阶段A 组件暴力拆解(glm.vision)<br/>产品侧×专利侧各6-12组件<br/>整体轮廓/部件/装饰/图案/色彩<br/>bbox 0-1000归一化 + 九宫格region"] --> D3
D3["组件分割标注图(PIL)<br/>bbox精确框 > region九宫格 > 自动网格兜底"] --> D4
D4["阶段B 逐组件对比(chat_json)<br/>形状/图案/色彩逐一比对<br/>相同/近似/不相同/不确定"] --> D5{"整体观察·综合判断<br/>overall_visual"}
D5 -->|identical/similar| D6["infringement"]
D5 -->|different| D7["non_infringement"]
D5 -->|由组件verdict回推| D8["uncertain"]
D6 & D7 & D8 --> D9["组件对比表+标注图+设计规避提示"]
end
IN["screened_patents<br/>(S3输出)"] --> INV
IN --> DES
I8 --> R["ClaimAnalysis → state.analyses[pn]"]
D9 --> R
7. 数据设计
7.1 FTOState —— 单体状态快照(Pydantic,schemas.py:99-137)
全部工作流状态序列化为一个 JSON 存入 tasks.state,即检查点快照本体:
输入(product_name/tech_description/jurisdictions/report_type/lang/图片/专利文件)
├─ S1: features[F-xx/D-xx{tier,search_hints双语}] + open_questions
├─ S2: search_strategies[{strategy_type,query_expr,hit_count}审计] + candidate_patents[Patent]
├─ S3: screened_patents + screen_matrix[{relevance,action}]
├─ S4: analyses{pn → ClaimAnalysis{claim_chart|components,components_visual,determination}}
├─ S5: risks[RiskItem{三维评级,overall,reviewer_override}]
├─ S6/S7: report_md + summary(zh/en) + qa_status/qa_issues
├─ 关卡: feature_confirmed / risk_confirmed / clarifications / report_edit_notes
└─ stage_runs[StageRun{agent,status,items_done/total}] 阶段台账
7.2 SQLite 表(store.py:14-84)
erDiagram
tasks ||--o{ events : "任务事件时间线"
tasks ||--o{ llm_calls : "LLM调用流水"
tasks ||--o{ chat_messages : "对话历史(含关卡卡片)"
tasks ||--o{ harness_runs : "对话运行轨迹"
tasks {
text id PK "T-XXXXXXXX"
text user_id "全链路关联"
text status "created/running/paused/awaiting_review/completed/failed/cancelled"
text stage "当前阶段"
text checkpoint "已完成检查点"
int pause_requested "暂停标志位"
text state "FTOState JSON快照"
text gate "当前待审关卡"
text signed_by "关卡③签发人"
real review_deadline "审批超时"
int run_count "重跑轮次"
}
events {
int id PK
text task_id FK
text type "stage_start/stage_done/stage_item/paused/gate_open/..."
text stage
text data "JSON载荷"
}
llm_calls {
int id PK
text stage "s1..s7/chat"
text name "glm.chat/glm.vision"
text model
int tokens_in
int tokens_out
}
chat_messages {
int id PK
text role "user/assistant"
text data "关卡卡片/工具结果载荷"
}
harness_runs {
text run_uid UK "H-xxxxxxxxxx"
text steps "轨迹JSON(llm_decision/tool步)"
text status "done/capped/guarded"
}
7.3 Redis Stream 令牌契约(token_metrics.py)
KEY: wf:llm_tokens (MAXLEN ~100000) XADD 9字段:
ts | user_id | matter_id | stage | name | model | tokens_in | tokens_out | total
- 双写策略:SQL 永远写(聚合查询事实源);Redis XADD 失联静默降级、30s 周期重连;
- 消费方式:
XREVRANGE(APIGET /tokens/stream)、前端 TokenStream 实时视图、XREAD BLOCK实时消费。
8. 模块职责与关键文件索引
| 文件 | 行数 | 职责 |
|---|---|---|
server/app/workflow.py |
1234 | ★ 引擎:7 阶段 handler、检查点、关卡开关、rerun、双模式比对、双报告模板 |
server/app/api.py |
506 | REST/SSE 全接口(任务/对话/审批/重跑/工作流全景/令牌/事件) |
server/app/dialog.py |
500 | 对话协调者:21 工具注册表 + 两段式执行 |
server/app/store.py |
304 | SQL 持久层(同步 + 全局锁,WAL) |
server/app/zhihuiya.py |
233 | 智慧芽 P002/P011/P010/P060 + Google Patents 抓取 |
server/app/images.py |
216 | 图片上传/降采样/base64 转换/PIL 组件标注图 |
server/app/llm.py |
179 | GLM 客户端(chat/chat_json/vision、截断重试、限流退避、mock) |
server/app/harness.py |
166 | 运行框架(守卫/预算/轨迹/回放) |
server/app/token_metrics.py |
147 | SQL + Redis Stream 生产者 |
server/app/schemas.py |
137 | Pydantic 数据契约 |
server/app/docs.py |
87 | 专利文件抽取(PDF/DOCX/TXT/MD)+ 专利号/权利要求段解析 |
server/app/config.py |
63 | .env 配置(26 项设置) |
web/src/components/* |
~1400 | 前端视图层(薄客户端,轮询 + SSE) |
9. API 分层设计
graph LR
subgraph 任务生命周期
A1["POST /tasks"] --- A2["GET /tasks?user_id="] --- A3["POST /{id}/start|pause|resume|cancel"]
end
subgraph 对话与审批
B1["POST /{id}/chat"] --- B2["GET /{id}/chat"] --- B3["POST /{id}/review"] --- B4["GET /{id}/harness[/{run_uid}]"]
end
subgraph 分析产出
C1["GET /{id}/workflow 全景"] --- C2["GET /{id}/report?lang="] --- C3["POST /{id}/rerun"]
end
subgraph 可观测
D1["GET /{id}/tokens"] --- D2["GET /tokens/stats"] --- D3["GET /tokens/stream"] --- D4["GET /{id}/events[/stream SSE]"]
end
subgraph 上传
E1["POST /uploads 图片≤10×8MB"] --- E2["POST /uploads/docs 专利文件≤6×20MB"] --- E3["/files/* 静态服务"]
end
10. 关键设计决策与亮点
- 快照式检查点:以
FTOState单体 JSON 为快照单位,检查点 = 阶段(S4 = 单专利)粒度,恢复天然幂等(pn in analyses跳过),实现极简且可靠; - 关卡即对话卡片:三个人工强制门与 chatbot 统一通道——同一
resolve_gate()同时服务自然语言裁决与 REST 审批,人机协同只有一个交互面; - 规则 × LLM 双层兜底:S3 分档失败回退 medium、S5 风险 LLM 失败走规则引擎(claim chart 统计评级)、S4 判定缺失由 chart 回推、S6 总体判定数字一律规则计算——LLM 只做叙述层,杜绝数字幻觉;
- 可观测性内建:每次 LLM 调用(chat/vision/对话)经
token_metrics.record()双写 SQLite + Redis Stream(9 字段契约),Harness 每步轨迹可回放; - 安全治理:输入守卫(提示注入启发式)+ 输出守卫(幻觉专利号检测)+ 步数/token 预算优雅收敛;
- 检索自愈:中英分路互不阻塞 → 零命中 LLM 泛化重试 → 以图搜图(design)多策略并行、各自审计留痕;
- 外观设计视觉管线:多图上传 → 降采样 base64 →
glm.vision暴力拆解(bbox/九宫格)→ PIL 标注图(三级兜底)→ 逐组件对比 + 整体观察综合判断,符合外观侵权判定司法框架; - 演示友好:
MOCK_LLM/MOCK_SEARCH全离线可跑;search_limit=4 / max_analyze=3控制规模成本。