资讯动态

Vibe Coding与LangGraph:AI时代编程范式的双螺旋演进

发布时间:2026/9/19 11:20:23 来源:尧图企业网站定制
1. 什么是“Vibe Coding”与“LangGraph”这不只是新名词而是编程逻辑的底层迁移你最近刷技术社区时大概率见过这两个词一个叫Vibe Coding另一个叫LangGraph。它们不是某个公司新推的SaaS产品也不是某家大厂刚开源的框架而是一种正在真实发生的、程序员写代码方式的结构性偏移——就像当年从面向过程转向面向对象或者从单体架构转向微服务那样这次偏移的驱动力不是人而是大语言模型LLM本身。Vibe Coding 的核心是把“写代码”这件事从精确语法驱动转向意图感知驱动。它不依赖 IDE 的自动补全、不靠静态类型检查、不靠单元测试先行而是靠你用自然语言描述“我想要什么”让模型去生成、解释、调试、甚至重构整段逻辑。比如你对模型说“帮我写一个 Python 脚本从本地 Excel 读取销售数据按城市分组求月均销售额画柱状图保存为 PNG路径用 config.yaml 里定义的 output_dir。”——这不是 prompt 工程这是在交付一个完整业务意图模型要自己拆解出文件 IO、pandas 操作、matplotlib 渲染、YAML 解析、异常兜底等全部环节。Vibe Coding 的“vibe”不是玄学而是指模型对上下文语义、领域惯例、工程约束的综合感知能力。它成立的前提是 LLM 具备足够强的 code generation reasoning self-correction 能力且开发者愿意放弃“逐行控制”的安全感转而信任模型对抽象意图的还原精度。LangGraph 则是这种新范式下的基础设施响应。它不是替代 LangChain而是重构了“如何组织 LLM 行为”的底层模型。LangChain 是以链Chain为中心输入 → Prompt 模板 → LLM 调用 → 输出解析 → 下一环节。它本质仍是线性流水线所有分支、循环、状态保持都靠外部逻辑硬编码。LangGraph 把“图Graph”作为第一公民节点是可执行单元可以是 LLM 调用、工具函数、条件判断、人工审核边是带条件的状态转移规则。你定义的不是“下一步做什么”而是“当状态满足 X 时流向节点 A当状态含错误码 Y 时流向节点 B 并重试”。这种建模方式天然适配 Agent 场景——比如一个客服 Agent收到用户问题后先做意图识别Node 1若判定为“查订单”则调用订单 APINode 2若返回空则触发历史对话回溯Node 3若仍无果则转人工Node 4。整个流程不是写死的 if-else而是由状态驱动的图遍历。LangGraph 的价值不在于多了一个新库而在于它把“LLM 行为编排”从 imperative命令式拉到了 declarative声明式层面。这两个概念放在一起看就构成了 AI 时代编程范式的双螺旋Vibe Coding 是前端——开发者用自然语言表达“做什么”LangGraph 是后端——系统用有向无环图DAG或有环图支持循环/重试来定义“怎么做”。它们共同指向一个结果程序员的核心工作正从“实现逻辑”转向“定义行为契约”和“校准系统反馈”。你不再花 3 小时 debug 一个正则表达式匹配失败而是花 20 分钟设计一个 retry 策略节点并观察它在 1000 次失败请求中是否真能收敛。这不是偷懒而是把人类最擅长的抽象建模能力从语法细节层拉升到系统行为层。这个转变对不同角色影响差异极大初级开发者可能更快上手业务脚本但若缺乏对状态流转、错误传播、token 边界等底层机制的理解极易写出表面跑通、实则脆弱的 Vibe Code资深架构师则必须重学“图建模”——不是 UML 活动图那种静态示意而是可执行、可观测、可热更新的运行时图谱技术管理者更需警惕当“写代码”门槛降低代码资产的可维护性、可审计性、可迁移性反而成为新瓶颈。我们接下来要拆解的正是这套新范式如何落地、为何必须重构、以及踩过哪些没人明说的坑。2. 从 Chain 到 Graph为什么 LangChain 不足以支撑真正的 Agent 系统2.1 LangChain 的设计原点与隐含假设LangChain 诞生于 2022 年底彼时主流 LLM 还是 text-davinci-003 和早期的 Llama-1。它的核心目标很务实降低 LLM 集成门槛让开发者能快速拼装出“Prompt LLM 外部数据源”的最小可行应用。为此它构建了一套高度抽象的组件体系LLM 接口、PromptTemplate、DocumentLoader、VectorStore、OutputParser……所有模块都围绕“一次调用、一次响应”这个原子操作设计。Chain 就是这些原子的线性组合——比如 SequentialChain 把多个 Prompt 拼接RouterChain 根据输入关键词选择子链MapReduceChain 对文档列表做并行摘要再聚合。这种设计非常成功但它背后藏着三个关键隐含假设调用是幂等且轻量的Chain 默认认为每次 LLM 调用耗时稳定、成本可控、失败率极低。所以它没有内置重试策略、超时熔断、token 预估机制。当你在 Chain 中嵌套 5 层 LLM 调用实际运行时可能因某次调用超时导致整个链阻塞而 LangChain 本身只抛出一个 generic Exception你得自己在每个节点外加 try-catch。状态是瞬时且局部的Chain 的上下文context本质上是字典传参所有中间结果都通过 memory 或手动传递。它不提供跨节点的状态快照、版本回滚、或状态变更监听。比如你写一个“多轮问答知识检索最终总结”的 Chain第 3 轮突然需要回看第 1 轮的原始提问就得在每个节点里显式存取 state 字典代码迅速变得像意大利面条。控制流是确定且单向的RouterChain 的路由逻辑基于字符串匹配或简单分类器一旦选错分支无法动态修正LoopChain 虽支持循环但终止条件只能是固定次数或布尔表达式无法根据 LLM 返回的结构化 JSON 动态决定是否继续。这意味着任何需要“LLM 自主决策下一步”的场景——比如 Agent 在工具调用后根据返回结果决定是继续搜索、还是生成答案、还是请求用户澄清——LangChain 都得靠外部 Python 逻辑硬编码彻底破坏了链的内聚性。提示LangChain 的 Chain 模式本质是把 LLM 当作一个增强版的函数调用。它适合“单次任务型应用”比如自动生成周报、批量改写文案、简单问答机器人。但一旦涉及“多步骤决策闭环”它就开始暴露架构短板。2.2 LangGraph 如何系统性解决这些问题LangGraph 的破局点是把“执行模型”从线性链升级为状态机驱动的有向图。它不否定 Chain 的价值而是将其降级为图中的一个节点类型RunnableCallable。整个系统围绕三个核心概念构建State一个可序列化的 Python 字典或 Pydantic 模型它是图中所有节点共享的唯一真相源Single Source of Truth。State 不是全局变量而是每次图执行时创建的独立副本确保并发安全。你可以定义 State 包含messages: List[BaseMessage]、tool_calls: List[dict]、retry_count: int等字段所有节点读写都通过这个接口。Node一个可调用对象function、class、Runnable接收当前 state返回更新后的 state。Node 可以是llm.invoke()调用tools[tool_name].invoke()工具执行lambda state: {retry_count: state[retry_count] 1}纯逻辑计算HumanInputRun()等待人工介入Edge定义节点间流转规则的函数。它接收当前 state返回下一个节点名str或节点对象。Edge 不是静态配置而是可执行逻辑。例如def should_continue(state): last_message state[messages][-1] if last_message.tool_calls: return call_tool # 有 tool_calls调用工具 elif FINAL ANSWER in last_message.content: return __end__ # 有终结标识结束 else: return agent # 否则继续 agent 思考这种设计带来质变重试不再是 hack而是图拓扑的一部分你可以定义一个retry_node它检查 state 中的error字段若存在则重置messages并增加retry_count再跳回agent节点。整个重试逻辑被封装在图中无需外部 try-catch。状态管理自动化State 字典由 LangGraph 运行时自动传递、合并、版本化。你不需要手动state.update(...)只需在 Node 函数中return {key: value}LangGraph 会 deep merge 到当前 state。更关键的是它支持checkpointer检查点可将 state 序列化到 Redis 或 SQLite实现真正的长周期 Agent如跨天的客服对话。控制流真正动态化Edge 函数可以任意复杂。你可以让 LLM 返回一个 JSON包含next_action: search和confidence: 0.87Edge 函数据此决定是否直接执行 search还是先调用clarify_question节点。这种“LLM 决策 → 图路由”的闭环才是 Agent 的灵魂。注意LangGraph 并非“LangChain 的下一代”。它和 LangChain 是平行关系LangGraph 甚至可以调用 LangChain 的 Chain 作为其一个 Node。官方明确表示LangChain 专注“组件复用”LangGraph 专注“行为编排”。两者互补而非替代。2.3 实战对比用 LangChain vs LangGraph 实现一个“天气查询 Agent”我们用一个具体例子验证差异。需求用户问“北京明天天气怎么样”Agent 需要识别地点北京和时间明天调用天气 API 获取数据若 API 返回错误重试 2 次若仍失败返回友好提示成功则生成自然语言回复LangChain 实现简化版from langchain.chains import SequentialChain from langchain.prompts import ChatPromptTemplate from langchain_openai import ChatOpenAI # 步骤1意图提取用 LLM extract_prompt ChatPromptTemplate.from_template(从用户问题中提取地点和日期返回 JSON {{location: xxx, date: xxx}}。问题{input}) extract_chain extract_prompt | ChatOpenAI() | JsonOutputParser() # 步骤2API 调用硬编码 def call_weather_api(location, date): # 模拟 API 调用可能失败 import random if random.random() 0.3: # 30% 失败率 raise Exception(API timeout) return {temp: 22, condition: sunny} # 步骤3生成回复 gen_prompt ChatPromptTemplate.from_template(根据天气数据生成一句话回复{weather_data}) gen_chain gen_prompt | ChatOpenAI() # 组合 Chain伪代码实际需大量 try-catch 和状态管理 def weather_agent(input_text): try: extracted extract_chain.invoke({input: input_text}) weather call_weather_api(extracted[location], extracted[date]) return gen_chain.invoke({weather_data: weather}) except Exception as e: # 重试逻辑必须手动写且无法记录失败原因 for i in range(2): try: weather call_weather_api(extracted[location], extracted[date]) return gen_chain.invoke({weather_data: weather}) except: continue return 抱歉天气服务暂时不可用问题显而易见重试逻辑散落在外层API 错误无法分类timeout vs 404 vs rate limit失败状态无法持久化整个流程无法观测每一步的输入输出。LangGraph 实现核心逻辑from langgraph.graph import StateGraph, END from typing import TypedDict, List, Dict, Any class WeatherState(TypedDict): input: str location: str date: str weather_data: Dict[str, Any] error: str retry_count: int def extract_intent(state: WeatherState) - WeatherState: # LLM 提取逻辑返回 {location, date} return {location: 北京, date: 明天} def call_weather_api(state: WeatherState) - WeatherState: try: # 实际调用这里模拟 data {temp: 22, condition: sunny} return {weather_data: data, error: } except Exception as e: return {error: str(e), retry_count: state.get(retry_count, 0) 1} def should_retry(state: WeatherState) - str: if state.get(error) and state.get(retry_count, 0) 2: return call_weather_api # 重试 elif state.get(error): return handle_failure # 失败处理 else: return generate_response # 成功生成 def handle_failure(state: WeatherState) - WeatherState: return {input: f抱歉天气服务暂时不可用{state[error]}} def generate_response(state: WeatherState) - WeatherState: return {input: f北京明天天气晴朗气温22度。} # 构建图 workflow StateGraph(WeatherState) workflow.add_node(extract_intent, extract_intent) workflow.add_node(call_weather_api, call_weather_api) workflow.add_node(handle_failure, handle_failure) workflow.add_node(generate_response, generate_response) workflow.set_entry_point(extract_intent) workflow.add_edge(extract_intent, call_weather_api) workflow.add_conditional_edges( call_weather_api, should_retry, { call_weather_api: call_weather_api, # 循环重试 handle_failure: handle_failure, generate_response: generate_response } ) workflow.add_edge(handle_failure, END) workflow.add_edge(generate_response, END) app workflow.compile() # 执行 result app.invoke({input: 北京明天天气怎么样}) print(result[input]) # 输出最终回复这个 LangGraph 版本的优势是结构性的可观测性每个节点的输入输出自动记录配合checkpointer可回溯任意时刻 state可扩展性新增“缓存检查”节点只需加add_node和add_edge不影响其他逻辑可测试性每个 Node 都是纯函数可单独单元测试可运维性retry_count字段天然支持监控告警如“重试超 2 次的请求占比”。这才是真正面向生产环境的 Agent 基础设施。LangChain 是胶水LangGraph 是骨架。3. Vibe Coding 的实践边界什么时候该信模型什么时候必须亲手写3.1 Vibe Coding 的有效前提三类高成功率场景Vibe Coding 不是魔法它依赖 LLM 在特定模式下的统计优势。经过上百次真实项目验证我发现它在以下三类场景中产出质量稳定、迭代效率显著高于传统编码第一类模板化数据管道Template Data Pipeline典型任务从 CSV/Excel/JSON 读取数据 → 清洗缺失值/格式 → 按规则分组聚合 → 导出报表或图表。为什么高效这类任务有强模式pandas 的read_csv,dropna,groupby,agg调用链高度标准化matplotlib/seaborn 的绘图参数plt.bar,plt.title,plt.savefig组合有限错误类型集中文件不存在、列名错误、类型转换失败。LLM 训练数据中充斥着此类代码生成准确率常达 95%。实操心得给模型的 prompt 必须包含具体约束而非泛泛而谈。例如不要说“画个柱状图”而要说“用 seaborn.barplot 绘制x 轴为 cityy 轴为 avg_sales标题为 各城市月均销售额保存为 output/sales_chart.pngdpi300”。约束越细生成越稳。我试过加一句“不要用 plt.show()因为这是服务器环境”就能避免 80% 的部署失败。第二类API 客户端封装API Client Wrapping典型任务为某个 RESTful API如 Stripe、Notion、Slack编写 Python SDK包含认证、错误重试、分页处理、返回值解析。为什么高效主流 API 的 OpenAPI SpecSwagger极其规范LLM 能精准映射path→method→params→response schema。它甚至比人类更擅长处理 OAuth2 token 刷新、rate limit header 解析等机械逻辑。实操心得直接把 API 的官方文档 URL 或 OpenAPI YAML 丢给模型比手写 prompt 更可靠。我曾让 Claude 3 读取 Stripe 的/v1/charges文档生成的ChargeClient类自带list()分页迭代器、create()的参数校验、retrieve()的 404 处理一行未改就通过了所有单元测试。但注意密钥管理、敏感日志脱敏、审计追踪等安全逻辑绝不能依赖 Vibe Coding必须手写。第三类UI 逻辑胶水层UI Glue Logic典型任务在 Streamlit/Gradio 中将 LLM 输出如 JSON 结构渲染为交互式表格、折叠面板、进度条或实现“点击按钮 → 调用 LLM → 流式显示 → 生成下载链接”的完整闭环。为什么高效这类代码本质是“事件绑定 状态更新 组件调用”框架 API 高度一致Streamlit 的st.button,st.session_state,st.download_button且错误反馈即时页面白屏立刻可知。模型能快速学会框架的“惯用法”。实操心得Vibe Coding 最怕“状态竞态”。比如 Streamlit 中st.session_state的修改必须在 callback 函数内完成。我最初让模型生成“点击后清空输入框”它写了input_text 这完全无效——正确写法是st.session_state.input_text 。后来我固定了 prompt 模板“所有状态操作必须使用 st.session_state.xxx yyy禁止直接赋值变量”。提示Vibe Coding 的黄金法则是——它擅长‘已知模式的组合’不擅长‘未知模式的创造’。当你需要设计一个全新算法、优化 O(n²) 时间复杂度、或解决分布式事务一致性时请关掉 ChatGPT打开 LeetCode。3.2 Vibe Coding 的四大高危雷区附真实翻车案例尽管 Vibe Coding 提速惊人但我在 3 个 SaaS 产品中踩过足够多坑总结出必须绕行的四大雷区雷区一浮点数精度与金融计算案例客户要求“计算订单总金额含 6% 税四舍五入到分”。模型生成total subtotal * 1.06 rounded round(total, 2) # ❌ 错误round() 在 Python 中对 .5 的处理是“银行家舍入”结果$1.235 → $1.23正确但 $1.245 → $1.24应为 $1.25。正确解法必须用decimal.Decimalfrom decimal import Decimal, ROUND_HALF_UP total Decimal(str(subtotal)) * Decimal(1.06) rounded total.quantize(Decimal(0.01), roundingROUND_HALF_UP)教训任何涉及金钱、科学计算、合规审计的场景Vibe Coding 只能生成初稿所有数值运算必须人工审查并替换为专业库。雷区二异步与并发控制案例需求“并发调用 10 个 API汇总结果”。模型生成import asyncio async def fetch(url): ... results await asyncio.gather(*[fetch(url) for url in urls]) # ✅ 正确看似没问题但漏了关键约束API 服务商限制 QPS ≤ 5。模型没意识到gather会瞬间发起 10 个请求。正确解法必须引入asyncio.Semaphore或aiohttp.TCPConnector(limit5)。教训Vibe Coding 对“资源约束”的感知极弱。它默认网络、内存、CPU 无限。所有涉及并发、限流、超时的代码必须手动注入约束参数。雷区三安全边界与输入验证案例用户上传文件模型生成def process_file(file_path): with open(file_path, r) as f: # ❌ 危险file_path 可能是 ../../etc/passwd return f.read()这是经典的路径遍历漏洞。模型知道open()但不知道os.path.abspath()和os.path.commonpath()的防御用法。正确解法必须用pathlib.Path(file_path).resolve().parent UPLOAD_DIR做白名单校验。教训Vibe Coding 生成的代码默认不具备安全防护意识。所有文件操作、SQL 拼接、OS 命令执行必须添加输入验证和沙箱隔离。雷区四LLM 自身的幻觉放大案例让模型“生成一个能解析 PDF 表格的函数”。它自信地写了import pdfplumber def extract_table(pdf_path): with pdfplumber.open(pdf_path) as pdf: page pdf.pages[0] return page.extract_table() # ❌ pdfplumber.extract_table() 返回 None 时很常见模型却当它总成功结果PDF 表格识别失败时函数直接返回None下游代码崩溃。正确解法必须加健壮性处理table page.extract_table() if not table: # 尝试 OCR 或返回空列表 return []教训当 Vibe Coding 的任务本身依赖另一个不确定系统如 PDF 解析、语音识别、图像检测时它会忽略该系统的失败概率生成脆弱代码。此时你的职责是给它加上“失败备选路径”。3.3 一套可落地的 Vibe Coding 协作流程团队已验证我们团队在 6 个月中将 Vibe Coding 整合进标准开发流程故障率下降 40%平均功能交付提速 2.3 倍。核心是建立三层协作机制第一层Prompt 工程化Prompt as Code我们不写自由文本 prompt而是用 YAML 定义“代码生成契约”# vibe_prompt/weather_agent.yaml task: 生成一个天气查询 Agent支持重试和错误处理 language: python framework: langgraph constraints: - 必须使用 TypedDict 定义 State - 重试次数上限为 2 - API 调用必须包装在 try-except 中 - 所有日志使用 logging.getLogger(__name__).info() examples: - input: 上海后天天气 output: {location: 上海, date: 后天}这个 YAML 文件和代码一起提交 Git确保 prompt 可版本化、可审计、可复现。第二层AI 生成 人工校验清单Checklist Driven Review每位工程师在接收 Vibe Code 后必须逐项核对[ ] 数值计算是否用decimal或numpy.float64替代float[ ] 所有外部调用API/DB/File是否有超时、重试、错误分类[ ] 输入参数是否做了长度、类型、范围校验[ ] 是否有敏感信息密钥、token硬编码[ ] 日志是否包含 trace_id 和关键上下文如 user_id, request_id这份清单贴在团队 Wiki 首页新人入职第一周就要背熟。第三层自动化护栏Automated Guardrails我们在 CI 流程中加入三道自动扫描Security Scan用 Semgrep 规则检测os.system(),eval(),pickle.load()等危险调用Precision Scan用 AST 分析器标记所有round(),float()强制改为DecimalConcurrency Scan检测asyncio.gather但无Semaphore的代码阻断合并。这套流程让 Vibe Coding 从“个人技巧”变成“团队能力”也印证了一个事实AI 不是取代程序员而是把程序员从语法劳工解放为系统校验者和契约设计师。4. LangGraph 实战从零搭建一个可商用的专利分析 Agent4.1 为什么专利分析是 LangGraph 的理想试验场专利分析业务天然具备 LangGraph 所需的全部特征强状态依赖、多工具协同、高容错要求、长周期交互。一个典型专利分析请求如“对比 US2023123456A1 与 CN123456789B 的权利要求差异”需经历步骤1解析专利号获取元数据国家、公开号、法律状态步骤2下载全文 PDF可能需代理或付费接口步骤3OCR 识别 PDF尤其 CN 专利常为扫描件步骤4提取权利要求文本XML/HTML/PDF 结构各异步骤5用 LLM 比较两组权利要求的覆盖范围、引用关系、技术特征步骤6生成可视化对比报告表格 关系图这个流程中任意环节都可能失败PDF 下载超时、OCR 识别率低于 70%、LLM 拒绝处理超长文本、权利要求解析正则失效……传统 Chain 模式下你得为每个环节写 fallback代码迅速失控。而 LangGraph 的图模型让“失败即路由”成为自然设计。我们以一个真实上线的专利分析 Agent 为例展示完整构建过程。它已服务 12 家律所日均处理 800 请求SLA 99.2%。4.2 状态设计定义 PatentAnalysisState可扩展的基石LangGraph 的力量始于 State 设计。我们没有用简单 dict而是定义 Pydantic v2 模型获得类型安全和文档自动生成from pydantic import BaseModel, Field from typing import List, Optional, Dict, Any class PatentMetadata(BaseModel): patent_id: str Field(..., description专利号如 US2023123456A1) country: str Field(..., description国家代码US/CN/EP) title: str Field(, description专利标题) status: str Field(unknown, description法律状态) class AnalysisResult(BaseModel): claim_diff: str Field(, description权利要求差异分析) similarity_score: float Field(0.0, description技术相似度 0-1) visualization: Dict[str, Any] Field(default_factorydict, description图表数据) class PatentAnalysisState(BaseModel): # 输入 query: str Field(..., description用户原始查询) patent_a: str Field(..., description第一件专利号) patent_b: str Field(..., description第二件专利号) # 过程状态 metadata_a: Optional[PatentMetadata] None metadata_b: Optional[PatentMetadata] None pdf_a_path: Optional[str] None pdf_b_path: Optional[str] None ocr_text_a: Optional[str] None ocr_text_b: Optional[str] None claims_a: List[str] Field(default_factorylist) claims_b: List[str] Field(default_factorylist) # 控制状态 current_step: str Field(start, description当前执行步骤) error: str Field(, description最新错误信息) retry_count: int Field(0, description当前重试次数) max_retries: int Field(3, description全局最大重试次数) # 输出 result: Optional[AnalysisResult] None class Config: arbitrary_types_allowed True这个 State 的设计哲学是字段命名直白pdf_a_path而非doc_path_1降低认知负荷Optional 显式标注强制开发者思考“这个字段何时为空”描述即文档Field(..., description...)会被 LangGraph 自动生成 API 文档控制字段分离current_step和error是运行时状态与业务数据隔离。注意State 模型一旦上线就成为 API 契约。新增字段必须考虑向后兼容如用Optional而非必填。4.3 节点实现每个节点都是单一职责的纯函数LangGraph 要求节点是无副作用的纯函数接收 state返回 state 更新。我们严格遵循每个节点只做一件事节点1解析专利号parse_patentsdef parse_patents(state: PatentAnalysisState) - dict: 从 query 中提取两个专利号支持多种格式 import re # 匹配 US2023123456A1, CN123456789B, EP3456789A1 等 pattern r\b(?:US|CN|EP|WO)\d[A-Z]{1,2}\b matches re.findall(pattern, state.query.upper()) if len(matches) 2: return {error: f未找到两个有效专利号仅识别到: {matches}} return {patent_a: matches[0], patent_b: matches[1]}为什么不用 LLM正则比 LLM 更快、更准、更便宜。Vibe Coding 不等于“所有事都让 LLM 做”而是“用最合适工具做最合适的事”。节点2获取元数据fetch_metadatadef fetch_metadata(state: PatentAnalysisState) - dict: 调用专利数据库 API 获取元数据 from patent_api_client import PatentAPIClient client PatentAPIClient() try: meta_a client.get_metadata(state.patent_a) meta_b client.get_metadata(state.patent_b) return { metadata_a: meta_a.model_dump(), metadata_b: meta_b.model_dump(), current_step: fetch_pdf } except Exception as e: return {error: f元数据获取失败: {str(e)}}关键设计节点返回{current_step: fetch_pdf}这是显式状态推进比隐式顺序更可控。节点3下载 PDFdownload_pdfsdef download_pdfs(state: PatentAnalysisState) - dict: 并发下载两个专利 PDF带重试 import asyncio from aiohttp import ClientSession async def _download_one(patent_id: str, save_path: str): async with ClientSession() as session: for attempt in range(state.max_retries): try: async with session.get(fhttps://api.patentdb.com/pdf/{patent_id}) as resp: if resp.status 200: with open(save_path, wb) as f: f.write(await resp.read()) return save_path except Exception as e: if attempt state.max_retries - 1: raise e await asyncio.sleep(1) return None # 并发执行 loop asyncio.get_event_loop() path_a, path_b loop.run_until_complete( asyncio.gather( _download_one(state.patent_a, f/tmp/{state.patent_a}.pdf), _download_one(state.patent_b, f/tmp/{state.patent_b}.pdf) ) ) if not path_a or not path_b: return {error: PDF 下载失败} return {pdf_a_path: path_a, pdf_b_path: path_b, current_step: ocr}为什么用 asyncio因为下载是 I/O 密集型同步会阻塞整个图。LangGraph 允许节点内使用任何异步逻辑。节点4OCR 识别ocr_pdfsdef ocr_pdfs(state: PatentAnalysisState) - dict: 调用 OCR 服务返回文本 from ocr_service import OCRService service OCRService() try: text_a service.extract_text(state.pdf_a_path) text_b service.extract_text(state.pdf_b_path) # OCR 可能返回空需校验 if not text_a.strip() or not text_b.strip(): raise ValueError(OCR 识别结果为空) return {ocr_text_a: text_a, ocr_text_b: text_b, current_step: extract_claims} except Exception as e: return {error: fOCR 失败: {str(e)}}节点5提取权利要求extract_claimsdef extract_claims(state: PatentAnalysisState) - dict: 用规则 LLM 提取权利要求 from llm_client import LLMClient llm LLMClient() # 先用正则粗筛快 def _regex_extract(text

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价