1. 项目缘起从“文档债”到“自动化”的救赎做后端开发的朋友尤其是用SpringBoot这类框架的对“接口文档”这四个字恐怕是又爱又恨。爱的是一份清晰、准确的文档是前后端高效联调的基石恨的是维护文档这事儿太磨人了。需求一变代码一改文档就得跟着更新。稍不留神文档就滞后了成了“文档债”。我自己在维护一个基于若依RuoYi框架的管理系统时就深受其苦。若依本身是个非常优秀的开源后台管理系统解决方案权限、菜单、代码生成一应俱全但接口文档这块还是得靠手动。传统的做法要么是在代码里写Swagger现在叫SpringDoc注解要么是单独维护一个Word或Markdown文件。Swagger注解确实能生成在线API文档但它有几个痛点第一注解侵入性强代码里到处都是ApiOperation、ApiParam看着有点乱第二生成的在线文档样式固定想导出为离线文档比如给测试或产品同学还得额外操作第三对于已经成型的、没有加Swagger注解的老项目 retrofit的成本不低。而手动维护的离线文档同步问题就更突出了。于是我就想有没有一种更“懒”但更有效的方法能不能让AI来干这个活儿我的核心诉求很明确输入是若依项目里原生的、干净的Controller层Java代码输出是一份结构清晰、可直接使用的Markdown格式接口文档。整个过程要自动化最好能集成到CI/CD流程里代码一提交文档就自动更新。这就是“Controller进Markdown出”的由来。它不依赖特定的注解而是通过分析代码结构、方法签名、参数名、返回值类型等元素结合大语言模型LLM的理解能力“读懂”代码并生成描述。2. 核心设计如何让AI“读懂”Controller这个项目的核心难点不在于调用某个AI接口而在于设计一套可靠的“翻译”流程把Java代码的结构化信息转化成AI能有效理解并生成高质量文档的提示Prompt最后再整理成标准的Markdown。整个过程可以分为四个核心环节代码解析、信息结构化、AI理解与生成、后处理与格式化。2.1 代码解析从.java文件到抽象语法树第一步我们必须把Java源代码从文本变成机器可理解的结构化数据。这里不能简单地用正则表达式去匹配因为Java语法复杂正则很难覆盖所有边界情况比如嵌套的泛型、复杂的注解等。最可靠的工具是抽象语法树。我选择了JavaParser这个库。它轻量、易用能完美地将一个.java文件解析成一棵AST。通过这棵树我们可以精准地定位到类定义获取Controller类的名称、RequestMapping或RestController注解中的基础路径/system/user。方法定义遍历类中的所有public方法。识别哪些是接口方法通常带有GetMapping,PostMapping,PutMapping,DeleteMapping,RequestMapping注解。方法细节提取方法的名称、返回类型、参数列表。对于每个参数获取其类型、参数名以及它可能携带的注解如RequestBody,RequestParam,PathVariable。特别要注意RequestParam的value或name属性这决定了URL参数名。注解信息虽然我们不强制要求Swagger注解但如果代码里已经写了ApiOperation或JavaDoc注释/** ... */这些是极佳的补充信息优先级最高应优先提取。// 示例使用JavaParser解析一个Controller方法 CompilationUnit cu StaticJavaParser.parse(new File(UserController.java)); ListMethodDeclaration methods cu.findAll(MethodDeclaration.class); for (MethodDeclaration method : methods) { // 检查方法上是否有Spring Web注解 OptionalAnnotationExpr getMappingAnnotation method.getAnnotationByName(GetMapping); if (getMappingAnnotation.isPresent()) { String methodName method.getNameAsString(); String returnType method.getType().asString(); // ... 进一步提取参数等信息 } }这个过程相当于给AI准备了一份关于接口的“原始数据清单”。2.2 信息结构化构建AI的“输入菜单”从AST提取出来的信息是零散的、面向语法层面的。我们需要把它们重新组织成一份对AI友好的“需求说明书”。我设计了一个简单的JSON结构来承载这些信息{ controllerClass: UserController, basePath: /system/user, interfaces: [ { name: getUserById, httpMethod: GET, path: /{userId}, returnType: ResponseEntityUserVO, parameters: [ { name: userId, type: Long, annotation: PathVariable, required: true, description: // 初始为空等待AI或JavaDoc填充 }, { name: format, type: String, annotation: RequestParam, required: false, defaultValue: json } ], javaDoc: 根据用户ID获取用户详细信息, rawCodeSnippet: public ResponseEntityUserVO getUserById(PathVariable Long userId, RequestParam(requiredfalse, defaultValue\json\) String format) { ... } } ] }这个结构化的JSON有几个关键点分离关注点明确区分了类级别信息basePath和方法级别信息。保留原始代码片段rawCodeSnippet字段非常重要。AI在理解复杂或自定义的参数类型如UserQueryDTO时光看类型名可能不够提供方法签名或相关类的定义片段能极大提升理解准确性。为AI留白description字段初始为空。我们的目标是让AI根据方法名、参数名、类型和原始代码推断出这个参数是干什么的。提示在构建这个结构时对于复杂的自定义对象如UserVO,UserQueryDTO最好能同时解析这些类的定义并将其字段信息也作为上下文提供给AI。这能避免AI对UserQueryDTO生成“用户查询数据传输对象”这种空洞的描述而是能具体到“包含用户名、手机号、状态等查询条件的对象”。2.3 AI理解与生成设计精准的Prompt这是项目的灵魂所在。我们不能简单地把JSON扔给AI说“写个文档”那样生成的内容会非常随意格式也不统一。Prompt工程在这里至关重要。我的Prompt模板大致如下它结合了系统指令、结构化数据和输出格式要求你是一个专业的Java后端开发工程师擅长编写清晰、准确的API接口文档。 请根据以下提供的Java Controller接口信息为每个接口生成详细的Markdown格式文档。 ## 接口上下文 - 项目框架SpringBoot 若依(RuoYi)管理系统 - 基础路径{{basePath}} - 当前Controller{{controllerClass}} ## 接口列表详情 {{#each interfaces}} ### 接口 {{index_1}}: {{this.name}} - **HTTP方法**: {{this.httpMethod}} - **路径**: {{this.path}} (最终URL为: {{../basePath}}{{this.path}}) - **返回类型**: {{this.returnType}} - **方法原始代码**: java {{this.rawCodeSnippet}}参数列表: {{#each this.parameters}}{{this.name}}({{this.type}}, {{this.annotation}}): [请根据参数名、类型、注解及代码上下文用一句话描述该参数的作用和规则。例如userId是路径变量表示用户唯一标识。] {{/each}} {{/each}}输出要求为每个接口生成一个独立的Markdown二级标题格式为## {{httpMethod}} {{basePath}}{{path}}。在每个接口标题下按顺序包含以下小节功能描述用一两句话概括这个接口是做什么的。请参考方法名和JavaDoc如果提供。请求参数以表格形式列出所有参数。表格列包括参数名、位置Path/Query/Body、类型、是否必填、默认值、说明。说明栏必须填写要结合业务逻辑进行推断。请求示例给出一个完整的、可读的请求示例如cURL命令。响应示例给出一个典型的成功响应JSON body示例。对于返回ResponseEntityT或RT的请展示T的数据结构。可能的错误码推断并列出几个常见的业务或系统错误码如400-参数错误404-资源不存在500-系统异常。所有描述性语言需专业、简洁、无歧义。整个输出请使用纯Markdown格式。这个Prompt的关键在于 - **角色设定**让AI进入“专业开发者”的角色。 - **提供充足上下文**框架、基础路径、原始代码这些信息能约束AI的想象使其生成的内容更贴合技术栈。 - **结构化指令**明确要求按接口拆分并规定了每个接口文档必须包含的子章节和格式特别是表格保证了输出的一致性。 - **引导推理**在参数部分不是直接给描述而是给出一个推理任务的描述“[请根据...用一句话描述]”这能激发AI的分析能力生成比“用户ID”更有价值的说明比如“用户的唯一标识符必须为正整数”。 ### 2.4 后处理与格式化从AI文本到标准文档 AI返回的是一大段Markdown文本。我们还需要做一些后处理工作使其成为一份真正可用的文档 1. **格式校验与修正**检查生成的Markdown是否符合规范表格是否对齐代码块语言标识是否正确。有时AI可能会漏掉某个小节可以用简单的规则进行补全或提示。 2. **信息融合**如果原始代码中已经包含了JavaDoc或Swagger注解后处理阶段应该用这些高可信度的信息去覆盖或补充AI生成的内容。例如JavaDoc中的param userId 用户主键ID就应该直接作为参数的最终描述。 3. **文档聚合**一个Controller会生成一个MD文件。我们可以设计一个索引生成器遍历项目所有Controller生成一个总的README.md包含所有接口文档的链接形成完整的API文档站点结构。 4. **集成与自动化**将整个流程脚本化Python或Java程序并集成到Maven/Gradle构建生命周期或Git的pre-commit/CI流水线中。实现“代码提交文档同步更新”。 ## 3. 技术选型与实战踩坑 有了设计思路接下来就是选型和实现。这里有几个关键决策点和踩过的坑。 ### 3.1 AI模型选择成本、效果与可控性的平衡 最初我尝试了云端大模型API如OpenAI的GPT-4或 Anthropic 的 Claude。它们的理解能力和生成质量确实很高对于复杂业务逻辑的推断很到位。但问题也很明显 - **成本**每次生成文档都需要调用对于接口数量多的项目是一笔持续的开销。 - **网络与延迟**依赖外部API在内网开发环境或CI流水线中可能受限。 - **数据安全**虽然只是代码片段但将公司项目代码发送到第三方云服务有些场景下存在合规风险。 因此我转向了**本地化部署的大模型**。目前有几个不错的选择 - **Ollama**极其方便的本地大模型运行框架一条命令就能拉取和运行模型。推荐使用 qwen:7b、llama2:7b 或 codeqwen 这类在代码理解上表现较好的模型。 - **LM Studio**图形化界面对不熟悉命令行的开发者更友好同样支持多种GGUF格式的模型。 - **DeepSeek-Coder** 等开源代码专用模型这类模型对代码的语法、语义理解更深生成文档时更准确。 **实操心得**对于接口文档生成这种任务对模型的“创造力”要求并不高反而对“准确性”和“格式遵从性”要求更高。经过测试6B-7B参数量级的模型在给出清晰Prompt的情况下完全能够胜任。本地部署的 Qwen-7B-Chat 或 CodeQwen-7B 模型是不错的选择它们在代码理解和指令跟随上表现良好且运行在消费级显卡甚至只靠CPU上也能接受。 ### 3.2 解析层的边界情况处理 在代码解析阶段会遇到各种“不标准”的写法AI生成流程的健壮性很大程度上取决于这里。 **坑1泛型与复杂返回类型** 若依框架常用 RT 或 ResponseEntityT 来包装返回结果。解析时不能只看到 R必须提取出泛型参数 T。 java public RPageInfoUserVO list(UserQueryDTO query) { ... }解析器需要能识别出PageInfoUserVO这个嵌套泛型并将其作为有效的返回类型信息传递给AI。否则AI可能只会描述“返回一个R对象”而不知道里面具体的数据结构。坑2参数绑定注解的多样性Spring提供了多种参数绑定方式解析器需要全面识别RequestParam(required false, defaultValue 0) int pageNumPathVariable(id) Long userId(注意注解内的别名)RequestBody Valid UserDTO user(可能带有校验注解)HttpServletRequest request,Model model(这类内置对象通常不需要写入文档)无注解的参数在Spring MVC中这可能被绑定到简单类型的请求参数上需要按RequestParam处理。坑3方法继承与接口实现有些Controller可能实现了某个基类或接口中的通用方法。单纯分析一个类文件可能漏掉这些方法。一个更健壮的方案是结合编译后的Class文件进行分析或者使用Spring自身的RequestMappingHandlerMapping在应用运行时获取所有端点信息。但对于静态分析工具来说前者更可行。3.3 Prompt工程的迭代优化最初的Prompt很简单结果AI经常“放飞自我”生成一些无关内容或者格式混乱。经过多次迭代才稳定到上文提到的版本。几个优化点明确拒绝指令在系统指令中加入“你只需要生成文档内容不要生成任何额外的解释、介绍或总结段落”有效避免了AI在文档前后加废话。示例的力量Few-Shot在Prompt中给一个完美的接口文档示例能显著提升AI输出的格式一致性。这就是“少样本学习”。分步骤指令将“分析参数”和“生成表格”分开要求比笼统地说“生成文档”效果更好。AI的思维链更清晰。温度Temperature设置对于文档生成这种需要确定性和一致性的任务将温度参数调低如0.1-0.3可以减少随机性让输出更稳定、可预测。4. 集成与落地让流程飞起来工具做出来最终目的是要用起来而且要无缝集成到开发流程中不能增加额外负担。4.1 本地开发一键生成我首先将其做成了一个Maven插件。开发者在本地执行mvn ruoyi-doc:generate插件会自动扫描src/main/java下所有标注了RestController的类。解析并生成结构化JSON。调用本地部署的Ollama服务通过HTTP API。将生成的Markdown文档输出到target/api-docs目录并按模块分文件夹存放。这样开发者在完成一个Controller的编写或修改后可以随时运行命令立刻看到最新的文档效果进行微调。4.2 持续集成自动化保障真正的威力体现在CI/CD流水线中。我们在GitLab CI或Jenkins中配置了一个Job监听master或develop分支的合并请求Merge Request。触发条件当MR的目标分支是master/develop且修改的文件包含*Controller.java时自动触发文档生成Job。执行流程CI Runner拉取代码运行Maven插件生成最新的Markdown文档。文档比对与提交将新生成的文档与仓库中文档目录如docs/api/下的现有文档进行比对。如果有变化则自动创建一个新的提交更新文档文件并推送到仓库。这个提交可以标记为[CI] Update API Docs。通知将文档更新后的预览链接如果部署了静态站点或变更内容摘要评论到MR中提醒代码审查者。这套流程彻底将开发者从手动维护文档的负担中解放出来确保了文档与代码的实时同步实现了“文档即代码”。4.3 效果展示与对比最终生成的Markdown文档格式清晰直接可以放入Wiki或部署为静态站点用MkDocs、Docsify等。以下是一个片段示例## GET /system/user/{userId} ### 功能描述 根据用户唯一标识符ID查询用户的详细信息。 ### 请求参数 | 参数名 | 位置 | 类型 | 必填 | 默认值 | 说明 | | :--- | :--- | :--- | :--- | :--- | :--- | | userId | Path | Long | 是 | 无 | 用户的主键ID必须为正整数。 | | format | Query | String | 否 | json | 响应格式可选值为 json默认或 xml。 | ### 请求示例 bash curl -X GET http://localhost:8080/system/user/123?formatjson \ -H Authorization: Bearer your_token_here响应示例成功{ code: 200, msg: 操作成功, data: { userId: 123, userName: zhangsan, nickName: 张三, email: zhangsanexample.com, phonenumber: 13800138000, status: 0, createTime: 2023-10-01 12:00:00 } }可能的错误码400 Bad Request: 请求参数无效如userId格式错误。404 Not Found: 指定ID的用户不存在。500 Internal Server Error: 服务器内部错误。对比手写或Swagger UI导出的文档AI生成的描述在“说明”一栏往往更贴近业务语义因为它尝试去“理解”参数名userId的含义而不是简单的技术描述。表格和示例的格式也非常规范统一。 ## 5. 局限性与未来展望 当然这个方案并非银弹目前仍有其局限性 1. **理解深度依赖代码清晰度**如果方法名和参数名起得随意如queryData(MapString, Object params)AI也难以生成准确的描述。良好的编码规范是基础。 2. **复杂业务逻辑的盲区**AI只能基于代码“签名”和有限的上下文推断功能。对于接口内部的复杂业务规则、状态变迁、权限校验细节等无法自动生成。这部分仍需人工补充说明。可以考虑扩展Prompt让AI在文档中提示“此接口涉及权限校验需要sys:user:view权限”但这需要从Spring Security注解中提取信息。 3. **模型的一致性**不同模型甚至同一模型的不同版本生成的结果可能有细微差异。需要通过严格的Prompt和后期模板来约束。 4. **非RESTful接口**对于WebSocket、GraphQL等非HTTP接口当前方案不适用。 未来的优化方向可以包括 - **多轮交互与人工修正**生成初稿后提供一个简单的界面让开发者可以快速审核、编辑AI生成的内容并将修正反馈给模型进行微调在线学习让模型越来越懂你的项目。 - **结合测试用例**如果能分析相关的单元测试或集成测试AI可以从中提取出更具体的请求/响应示例甚至包括边界情况和错误场景。 - **生成多种格式**除了Markdown是否可以一键生成Postman Collection、OpenAPI 3.0 (Swagger) spec文件满足不同工具链的需求。 这个项目的核心价值不在于替代开发者思考而在于**将开发者从重复、机械的文档编写劳动中解放出来**让他们能更专注于代码逻辑和业务创新。它证明了在软件开发中那些看似需要“人类智能”的繁琐任务正逐渐可以被AI以一种可靠、高效的方式接管。“Controller进Markdown出”只是一个开始。