1. 为什么做这个项目当“写登录脚本”变成高频重复劳动先说说这个项目的真实来源。在很长一段时间里我发现自己不断地在写同一类代码登录某个系统保持会话再去做后续操作。不管是做自动化测试、写爬虫采集公开数据还是帮内部系统做巡检脚本第一步永远是登录。麻烦的地方在于每个系统的登录逻辑都不一样——有的走表单提交有的要先请求一个token再带参访问有的需要处理动态加载的验证码有的则是扫码登录或者短信验证码登录。写一次两次还能忍受写多了就变成纯粹的体力活。每次都要花时间分析登录接口、看参数怎么构造、摸清cookie和session的交互方式。遇到验证码还得单独研究怎么识别或绕过。这时候我冒出一个想法能不能让大模型帮忙干这个活输入一句“用账号密码登录某系统保持登录态并输出cookie”它直接给我生成一个能用的Python脚本。这就是“LangChainPython实现登录脚本智能生成”这个项目想解决的核心问题。整个项目用LangChain作为大模型应用框架Python作为执行语言核心目标是从用户的自然语言描述中解析出登录场景推理出合理的技术方案最终生成可运行、可校验的登录脚本。它适合谁参考如果你经常写自动化脚本或者团队里总有人反复问“登录怎么写”再或者你想把“根据需求自动生成工具脚本”这件事做成一个小工具这个项目的思路都可以直接借走。本质上这个项目不是要做“万能登录生成器”而是搭建一套“自然语言到可执行代码”的流水线登录脚本只是第一个落地场景。后面你会发现摸清楚这条流水线的关键环节之后把场景换成“生成数据清洗脚本”或者“生成文件批处理脚本”思路完全不用改。2. 整体方案设计从自然语言到可运行代码的完整链路2.1 核心架构把“生成代码”拆成四个环节我最早犯的错误是觉得“这不就是让大模型生成一段代码吗”直接写一个Prompt丢进去就行。但实际做起来发现问题很多大模型生成的代码有时候是用不到的假函数有时候用的是不存在的库有时候解析网页的方式完全跑不通。真正能用的方案是把流程拆成四段各自负责一块意图解析从用户描述中提取登录类型、目标系统特征、需要的登录态形式。方案推理基于意图选择合适的技术栈requests还是Selenium还是Playwright设计登录流程步骤。代码生成按方案逐步生成Python代码每一步都带注释。校验修正对生成的代码做静态检查和基础运行验证发现问题后让模型自行修正。LangChain在这套架构里起到了两个作用一是把多个Prompt调用串联成流水线用LCEL语法或者其他链式写法把“解析—推理—生成—校验”串起来二是通过结构化输出解析功能比如PydanticOutputParser让模型返回的内容稳定可用。为什么一定要结构化输出这是我在实际踩坑后得出的结论如果你直接让模型“给出代码”它会在代码块之外还输出一大堆解释甚至有时候解释写在代码里复制出来根本没法运行。用结构化输出的方式强迫它返回JSON{login_type: ..., tech_stack: ..., steps: [...], code: ...}。这样后续每个环节都能拿到干净的数据。2.2 技术选型LangChain的哪些模块被真正用到LangChain库很大但这次项目里真正用的模块其实就那么几个。PromptTemplate负责管理提示词模板把变量安全地嵌入到模板中。PydanticOutputParser负责声明输出结构把大模型的返回文本解析成Python对象。这两者组合之后相当于给大模型加了一层“格式约束”。实测下来使用结构化输出后解析失败率从最初的20%以上降到了5%以内——这个数字非常关键因为做自动化流程时一旦某个环节解析失败整条链就断了。还有一个模块是ChatOpenAI也就是大模型接口的封装层。这里要说明一下项目本身不绑定某个具体大模型服务只要是兼容OpenAI接口格式的模型都可以换上去。实际使用中我发现代码生成场景下参数量越大的模型越稳因为代码生成涉及多重逻辑推理和上下文依赖模型太小容易生成“看起来对、跑起来错”的代码。2.3 为什么用“分步生成”而不是“一次性生成”这是整个项目中最重要的一次方案取舍。对比做过两组实验一组是让模型一次性输出完整登录代码另一组是先让模型输出“登录步骤清单”再根据步骤逐段生成代码。一次性生成的问题是当系统登录流程比较复杂时比如先获取验证码图片再识别验证码再携带验证码提交表单模型很容易把某个环节写错——常见的是验证码识别函数里硬编码了一个错误的图片路径或者把HTTP请求的data参数和json参数搞混。一次性叫出来的代码表面上完整细节上一堆小错误。分步生成就稳妥得多。先让模型写出一个步骤清单这一步相当于把复杂问题拆解成模型更擅长处理的小问题。然后针对每一步单独生成代码片段最后按顺序拼接。这个思路其实跟人类程序员的工作方式是一样的先想清楚有几个步骤再一步一步写。实测下来最终代码的可运行率从30%左右提升到了70%以上。2.4 需要考虑的登录类型覆盖范围设计之初我把常见的登录类型列了一遍至少要覆盖下面这些表单登录账号密码提交到后端接口基于Cookie的会话保持登录基于Token的Bearer认证登录带图片验证码的登录短信验证码登录需要用户手动输入扫码登录生成二维码后轮询状态每一种登录类型的代码模板差异很大。如果让模型自己“临场发挥”写出来的代码风格和逻辑会很不稳定。我的做法是把这些通用模板写进Prompt里作为Few-shot示例的一部分让模型在这些示例的框架内做修改和组合。实验发现加入示例模板后生成代码的稳定性和风格一致性提升明显。这个设计细节值得展开说。所谓“示例模板”不是让模型照抄而是给它提供一个“骨架”。比如表单登录的骨架是构造Session - 获取必要的隐藏字段 - 提交表单 - 校验响应状态 - 返回Session。模型的作用是根据具体目标系统的情况调整URL、参数名、请求头。有了骨架生成结果就不会跑偏到“用Scrapy去登录一个纯前端应用”这种离谱方向。3. 实操环节一步步搭建登录脚本生成器3.1 环境准备与依赖版本项目用到的核心库有五个langchain、openai或其他兼容SDK、pydantic、requests、beautifulsoup4。另外建议安装playwright因为大量现代网站的登录逻辑依赖JavaScript渲染纯requests搞不定。虚拟环境方式安装Python版本建议3.10以上python -m venv venv source venv/bin/activate # Windows下是 venv\Scripts\activate pip install langchain langchain-openai pydantic requests beautifulsoup4 playwright playwright install chromium这里的playwright install chromium很容易踩坑。如果你在国内网络环境下执行下载浏览器内核可能会失败。解决方式是手动下载浏览器包放到PLAYWRIGHT_BROWSERS_PATH指向的目录。这一步很多人没做后面程序卡在playwright调用浏览器找不到文件的位置。3.2 定义意图解析的结构化输出先定义意图解析的输出结构也就是告诉大模型“你返回的结果必须长这样”。这里用Pydantic定义from pydantic import BaseModel, Field class LoginIntent(BaseModel): target_site: str Field(description目标系统名称/URL特征) login_type: str Field(description登录方式form / cookie / token / captcha / sms / qrcode / 组合) tech_stack: str Field(description建议使用的技术栈requests / selenium / playwright) session_format: str Field(description需要输出的登录态形式cookie / token / session对象) special_requirements: list[str] Field(description特殊要求列表例如验证码处理、代理设置、UA伪装等)定义好这个模型之后要写对应的提示词模板明确告诉模型“你将收到用户对登录任务的描述请解析出以下JSON字段……不要输出解释。”实际测试中Field(description...)里的描述非常重要。模型的输出质量直接受这些描述影响。描述越具体输出越准确。比如login_type如果只写“登录方式”模型可能返回“账户密码登录”这种非标准化描述写清楚“必须是form/cookie/token/captcha/sms/qrcode中的一种或组合”模型就能严格按枚举值返回。3.3 解析器的接入方式使用LangChain的PydanticOutputParser把它和PromptTemplate绑定from langchain.output_parsers import PydanticOutputParser from langchain.prompts import PromptTemplate from langchain_openai import ChatOpenAI parser PydanticOutputParser(pydantic_objectLoginIntent) prompt PromptTemplate( template请解析用户的登录任务描述提取结构化信息。\n{format_instructions}\n用户描述{user_input}\n, input_variables[user_input], partial_variables{format_instructions: parser.get_format_instructions()}, ) llm ChatOpenAI(modelgpt-4o-mini, temperature0.1) chain prompt | llm | parser result chain.invoke({user_input: 帮我写一个登录某系统的脚本账号密码登录登录后保存cookie到文件}) print(result.login_type) # form这里有个关键参数temperature0.1。生成代码场景不适合高温度高温度会让模型发散输出不稳定。我试过temperature默认的1.0生成的代码三次有两次不一致降到0.1之后基本稳定。但也不能设置为0完全0会让模型过于机械在需要一些灵活推理时会表现笨拙。另外LangChain这里用了管道运算符|这是LCEL的写法。调用流程很清晰定义先格式化Prompt再调用模型然后解析输出。如果某一步失败错误信息会指明是解析失败还是模型调用失败排查起来比较方便。3.4 代码生成环节的提示词设计意图解析完成之后接下来是代码生成。这个环节的提示词设计直接决定最终脚本能不能跑。我的做法是把提示词分成四块身份定义、任务目标、技术约束、输出格式。code_gen_prompt PromptTemplate( template 你是一位精通Python自动化脚本开发的工程师。请根据以下需求生成登录脚本。 目标系统{target_site} 登录类型{login_type} 技术栈{tech_stack} 登录态要求{session_format} 特殊要求{special_requirements} 生成代码时必须遵守 1. 代码必须完整可运行包含所有import语句。 2. 必须使用try/except包裹关键网络请求并打印明确的错误信息。 3. 如果涉及验证码生成代码时用占位符表示例如 captcha_code input(请输入验证码: )。 4. 不允许生成伪造的函数所有调用的函数必须在代码中定义了完整实现。 5. 最终只输出Python代码不要输出解释、不要使用markdown代码块标记。 需求描述 {user_input} 生成的代码 , input_variables[target_site, login_type, tech_stack, session_format, special_requirements, user_input], )第四个约束“不允许生成伪造的函数”是我加了多次测试后筛选出来的。不加这句话时模型偶尔会生成一个调用login_parser()的代码但login_parser函数本体不存在。加上这句话之后再配合校验环节的检查基本能堵住这个问题。3.5 使代码可直接落地的关键细节拿到模型生成的代码后不能直接写文件就跑。需要做几层处理第一剥掉外壳。有时候模型仍然会输出markdown代码块标记python ...需要正则去掉import re def extract_code(text: str) - str: pattern r(?:python)?\s*(.*?) match re.search(pattern, text, re.DOTALL) if match: return match.group(1).strip() return text.strip()第二做基础语法检查。用Python自带的compile()函数编译代码能立刻发现缩进错误、括号不匹配等明显问题。def validate_code_syntax(code: str) - bool: try: compile(code, generated, exec) return True except SyntaxError as e: print(f语法错误: {e}) return False第三更近一步的静态检查是检查import的库是否存在。很多时候模型会import一个不存在的库或者写错库名。用一个简单的办法遍历ast解析出的import节点挨个尝试import。这一步能拦截掉大概10%的无效代码。这一步我自己写了一个小函数用ast模块解析代码拿到所有import的模块名然后在当前环境里尝试导入比新建进程快得多。如果某个库不存在就把错误信息反馈给模型让它修正导入或改用其他方案。这个“检测—反馈—修正”循环是这个项目里提升代码可用率的最大功臣。第四检查代码中是否包含“TODO”“pass”“NotImplementedError”这类明显未完成的标记。如果发现直接判定为不合格触发重新生成。3.6 整条链路的代码组织把意图解析、方案推理、代码生成、校验修正串起来主流程的伪代码大致长这样class LoginScriptGenerator: def __init__(self, llm): self.llm llm self.intent_parser build_intent_chain(llm) self.code_chain build_code_gen_chain(llm) self.validator CodeValidator() def generate(self, user_input: str) - GeneratedScript: intent self.intent_parser.invoke({user_input: user_input}) attempts 0 while attempts 3: code self.code_chain.invoke({ target_site: intent.target_site, login_type: intent.login_type, tech_stack: intent.tech_stack, session_format: intent.session_format, special_requirements: intent.special_requirements, user_input: user_input, }).text code extract_code(code) issues self.validator.check(code) if not issues: return GeneratedScript(intentintent, codecode, issues[]) attempts 1 # 把issues反馈给模型进行修正 correction_prompt CorrectionPrompt(...) code self.llm.invoke(correction_prompt).content return GeneratedScript(intentintent, codecode, issuesissues)注意这里的while循环最多重试三次。这背后有一个实测结果如果三次修正仍然无法通过校验大概率是这段需求本身描述不够清晰或者需要的技术超出了当前模型能力。继续重试只会浪费时间不如把问题抛回给用户去补充信息。3.7 引入真实的交互体验对用户来说使用方式最好简单直接。我在项目里加了一个命令行交互入口$ python main.py 请输入你的需求描述: 登录某网站账号密码登录登录后把cookie保存到cookies.json后续请求要用这个cookie 正在解析需求... [解析结果] 登录类型form, 技术栈requests, 登录态cookie 正在生成脚本... [生成结果] 已生成 login_script.py通过了语法检查和依赖检查 是否尝试运行需要该网站可达(y/n): n这个交互环节看起来不起眼但极大地提升了项目的实用感。快到验证阶段时用户只需要输入一句话几十秒后就能得到一个能运行的、带依赖检查和异常提示的脚本。这里的核心体验是你把用户从“我要自己分析怎么登录”这个任务中解放出来了。4. 踩坑实录几个最影响生成质量的细节4.1 模型输出中的“虚假模块”问题这是最让我头疼的问题没有之一。模型会生成看起来完全合理但实际不存在的Python模块。比如它可能用到一个叫做httpx_auth的库但这个库并未安装甚至它声称某个函数来自requests_html这个库确实存在但经常被误用。校验环节用ast遍历import后尝试导入可以拦下一大部分。举一个实际经历有一次测试模型生成了一段代码import了fake_useragent这看起来是一个处理UA的常见库但它也正确注册了一个自定义UA生成逻辑。问题是这个库本身依赖环境里没有的pyyaml导致隐式依赖缺失。后来我在校验环节不仅检查了import语句还检查了import库的间接依赖是否完整。做法很土就是在一个干净虚拟环境里跑pip install如果失败就告诉模型换方案。实测下来这类生成结果的修复率很高。4.2 验证码处理的策略登录脚本里最复杂的部分是验证码。无验证码时登录逻辑相对直白。一旦涉及验证码模型的输出经常不稳定。常见的问题是生成代码里调用了一个不存在的OCR库或者把验证码图片URL写成一个近似错误的拼接。我在设计时定了两条原则一是默认情况下不要自动识别验证码而是生成一个交互式输入让脚本在运行过程中暂停由执行者去系统里查看验证码再输入二是如果目标系统确实有开放的验证码识别服务生成时以占位符方式预留接口。第二条原则相当于告诉模型“你不必真的去解决验证码识别问题你只需要在这里留一个明显的接口位置”。这背后有一个教训不要试图让语言模型“幻想”出验证码识别能力它不具备图像处理和验证码推理的实质能力除非它内部集成了相应工具。在架构上明确边界比构造一个看似聪明但实际不可行的流程聪明得多。4.3 动态网页与JavaScript渲染很多“登录系统”不是传统的表单提交而是前端渲染的动态页面。用户描述可能只是“打开某网站点击登录按钮输入账号密码点击提交”。如果技术栈误选了requests后面会撞上一堆问题——因为requests无法执行JavaScript。这个项目里我处理的方式是在意图解析阶段额外要求模型判断目标系统是否需要JavaScript渲染。判断依据是用户描述里有没有“打开页面”“点击”这类交互性词汇。但更稳妥的做法是在生成完代码后让用户看到技术栈选型并提供一个手动切换选项如果用户反馈“这个网站是单页应用”就强制切换到Playwright技术栈重新生成。一开始我没有这个手动选项失败率特别高。后来我专门加了一个参数force_engine允许用户指定必须使用Playwright或者必须使用requests。这个开关看似简单实际把复杂场景救了回来增加最终成功率20个百分点以上。4.4 长代码生成时的上下文截断问题验证码系统描述比较复杂时比如“登录后还要经过一次MFA校验”一次生成长代码很容易出现上下文截断或者逻辑断裂。我原来的链是直接输入整段需求让模型从头生成到尾。后来改成“分步生成、逐步拼接”把需求拆成“获取验证码图片→识别或输入验证码→提交登录→保存会话”四段每一段单独生成代码再拼接。拼接不是简单字符串相加。分步生成时后一步会依赖前一步的某个变量名比如session、csrf_token。如果不加约束两步生成的变量名可能不一致。我的处理方式是中间加一个“整合”环节把各段代码交给模型一次让它统一变量名形成最终脚本。虽然多了一次模型调用但产出的代码一致性提升非常多。4.5 模型对目标URL的“幻觉”有一个特别隐蔽的问题当用户描述一个知名网站时模型会“自由发挥”它记忆里的URL或接口路径但这个URL往往早已失效或者根本是不完整的。比如有一段描述是“登录一个内部的运维平台”模型生成的代码里URL字段居然是http://10.0.0.1/login显然不真实。我后来在意图解析结构里加了一个字段url_unclear并在流程中加入一个二次确认机制如果模型认为目标URL不确定就会在生成的代码里写BASE_URL input(请输入目标系统URL: )运行时让用户自己填。这个改动虽然降低了“自动化程度”但大幅提升了脚本的可用性。反过来想大模型再强也并没有真实访问目标系统的能力它只是参考记忆来拼装所以在关键URI上保持开放性输入才是稳妥的选择。5. 常见问题排查速查与安全边界到这里我把使用过程中经常撞到的问题整理成表格方便直接查阅。这些问题的排查思路都经过实践检验尤其是表中前三条基本覆盖了八成以上的失败场景。现象首次快速检查更根本的解法生成的代码有import的模块找不到检查环境是否安装该库在校验环节加入AST import检查并回传模型修正代码可以编译但运行时报连接错误确认目标网站可达、URL是否写死改为运行时input()输入URL或启动交互确认机制验证码步骤无法实施查看是否是OCR库不存在或识别效果差改为交互式手动输入验证码不要在生成层强行解决识别技术栈选型错误该用Playwright用了requests确认目标是否前端渲染增加force_engine参数允许用户指定技术栈多次重试仍失败确认需求描述是否完整缺少URL缺少登录方式将缺失信息问题的反馈原样展示给用户发起一次澄清问答模型返回包含Markdown代码块导致解析失败检查提取代码的正则逻辑在提示词中明确“不要用代码块标记”并在解析阶段做剥离长代码生成中途逻辑断裂查看是否有半截函数、未闭合的定义改成分段生成后拼接最后加整合统一变量名这里有一点不得不强调这个项目生成出的脚本一定要在受控环境里先测试不要直接拿着它去登录一个生产系统。一方面生成脚本的对象是“自动化操作”如果对登录接口的调用频率过高可能给目标系统造成不必要的压力另一方面登录态本身可能涉及账号安全性随便用生成脚本去跑别人的系统很可能触发风控弄不好还会把自己的账号弄封。我的习惯是生成完先本地review再看那个目标系统允不允许自动化操作最后才在测试环境执行。安全边界这块还有一个容易被忽视的细节不要把真实的账号密码直接硬编码进生成的脚本。在提示词里我也会约束模型密码字段必须用os.getenv(LOGIN_PASSWORD)这种方式来读取或者用getpass让运行时输入。这是基本的安全习惯但大模型默认不会主动这么写需要你明确在Prompt里塞进去。我在第一批实测脚本时就踩了一次坑——生成的代码里直接明文写密码赶紧改掉了。6. 代码结构里藏着的几个工程细节6.1 如何组织保存生成的脚本生成的代码不应该只是简单落盘。我在项目中加入了一个元信息头自动写在生成脚本的顶部注释里# 自动生成时间: 2025-xx-xx # 生成参数: login_typeform, tech_stackrequests # 说明: 该脚本由语言模型生成请在测试环境验证后再使用不要小看这几行注释。没有元信息时拿到脚本的人会以为这是一个手工写的脚本发现问题会去逐行debug这浪费了大量精力。标注清楚来源后后续维护时能很快判断问题方向。6.2 依赖记录与运行隔离生成的脚本如果需要额外的依赖比如某个特定版本的库我会生成一个requirements.txt片段放在脚本同目录。这样使用者不需要手动去“猜”缺哪个库。更稳妥的做法是有一个scripts/venv/目录每次生成的脚本都在独立的虚拟环境中运行完全隔离依赖。我一开始觉得虚拟环境是多余动作但后来自己又生成了一段时间的脚本发现如果不隔离早晚会有一个库版本冲突出来制造惊喜。隔离之后生成脚本的可复现性好了非常多。6.3 多轮对话能力最初的版本只支持单次描述、单次生成。后来我发现用户往往需要和“生成器”对话才能说清楚需求“我要登录某网站……嗯那个网站要先滑动验证……对是滑块验证码不是图片验证码……另外登录后还要跳转一个二次页面确认。”这种信息是一步步挤牙膏挤出来的。基于这个观察我在系统中加入了简单的多轮对话继承功能把历史消息塞入上下文让模型在其中识别“新增”和“变更”的信息。这不算复杂但实在提升体验。如果你在复现这个项目我建议在意图解析阶段就把历史摘要包含进去——不要只依赖用户最后一次输入。7. 个人体会与可扩展方向做了这个项目之后我最大的体会是LangChain在这里不是一个“妙不可言”的框架而是一个把“模型调用—提示词管理—输出解析—流程控制”组织起来的实用工具。它真正的价值不是封装了一个多厉害的AI接口而是逼着你把任务拆成有边界的模块并让每一步的输出都有结构和逻辑可校验。与其说这是在做AI应用不如说是在做工程化。登录脚本智能生成的链路一旦跑通往旁边扩展的想象力就打开了。我在项目结束前做了几个实验换掉登录场景改成了“生成文件批处理脚本”和“生成自动化测试报告脚本”完全复用同一套架构只替换了提示词模板和输出结构定义。换场景的成本低到出奇几乎就是换一个意图解析的Schema再加一批示例的事。一句话总结这个项目里头真正值钱的不是登录脚本本身而是那条“自然语言描述—结构化意图—分步代码生成—静态校验—迭代修正”的管道。你随时可以把管道出口的东西换掉管道本身成了复用资产。有一点我必须再强调一遍校验环节是整条管道的灵魂。没有校验大模型会给你一个看上去很美、实际上跑不起来的“幻觉代码”。有了校验它才真正变成了一个工程环节和可靠工具。每次上报“校验不通过”的附加信息都会让模型在下一次生成时更加谨慎。这也是我建议后来者最先着手优化的部分——不要急着换更大的模型先把这层“质检网”织牢。