做前端内容创作Agent这个项目,起因其实很朴素:我发现日常开发里“写页面”这件事,有六成以上的时间都消耗在机械性劳动上。从设计稿还原、组件拼装、列表表单到文案填充,这些工作逻辑并不复杂,但它吃掉了我大量的精力。回头复盘时我意识到,如果能把LLM接到前端开发流程里,让它按我习惯的组件库和规范生成初稿,我只需要做核查和调整,效率能翻一倍不止。这篇文章就是我在落地这个方案时的完整设计笔记,包括选型推理、系统架构、核心模块拆解、模型部署和踩坑记录。它适合正在规划Agent项目的技术负责人、对LangChain生态有兴趣的开发者,以及想用开源模型做私有化智能体的团队参考。
1. 需求边界与目标定义:这个Agent到底要解决什么问题
很多Agent项目失败,根源不是技术不行,而是需求边界从一开始就模糊。我的第一步不是写代码,而是把“前端内容创作”拆成计算机能理解的约束条件。
1.1 前端内容创作的痛点在哪里
前端领域的“内容创作”和写文章、画图完全不同。它有几个非常鲜明的特点:
- 强语法约束:输出必须是符合语言规范的代码,差一个括号都无法运行。
- 强组件依赖:企业内部通常有自己的组件库,生成结果必须基于已有组件体系,而不是让LLM自由发挥手写div。
- 风格一致性:颜色、间距、字体、交互模式都要符合既定设计规范,否则出来的页面是拼贴画。
- 可运行验证:文本内容错了可以靠人眼检查,代码生成却必须经过编译、执行、视觉验证。
这些特性决定了,如果直接打开一个随意的对话窗口让LLM“帮我写个表单页面”,得到的代码大概率是能看但没法用的。因为LLM不知道你用什么组件库、什么版本、请求接口长什么样、风格规范具体怎么描述。
1.2 我期望的交互形态
经历了两个星期的需求梳理,我把这个Agent的核心交互定义成一句话:“用一句话描述一个页面/组件/功能区块,Agent调用合适的开源模型,在本地私有化环境里先检索已有的组件与代码示例,再生成符合规范的代码,最后自动又拍校验修复后交付。”
具体覆盖四类场景:
- 整页生成:输入“生成一个用户列表页面,包含搜索、分页、状态筛选,操作列有编辑和禁用”,输出一个可直接运行的Vue/React页面。
- 区块开发:输入“做一个价格对比卡片,三个档位,中间高亮推荐”,输出可嵌入的组件块。
- 组件封装:输入“封装一个支持防抖的搜索输入框”,输出完整组件代码和使用Demo。
- 代码解释与重构:输入一段现有代码,让Agent解释逻辑或按新规范重构。
需求边界清晰后,很多技术选型问题会自己浮出水面,而不是先选技术再套应用。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 选型决策的完整推理链路:LangChain、LangGraph与开源模型
选型是很多人最容易纠结的环节。作为一个跑过多个Agent项目的实践者,我的建议是:先看应用形态,再定框架,最后定模型。
2.1 LangChain和LangGraph到底选哪个
“LangChain和LangGraph的区别”是社区里问得最多的问题。我早期的项目用的是LangChain的AgentExecutor和initialize_agent,后来重构成LangGraph。两者的核心差异在控制粒度。
LangChain更像 工具集合 + 简易Agent循环,优点是上手快,文档丰富,内置大量接口封装。缺点是当你的Agent逻辑复杂到需要精确控制每一步的状态流转时,AgentExecutor就像一个只能做“提问-调工具-再提问”的自动循环,分支、并行、回退这些逻辑写起来很别扭。
LangGraph则把Agent定义成一个带状态机的图,每个节点是可执行函数,每条边是状态转移条件。它的核心API是StateGraph。你可以精确定义:
- 什么时候进入代码生成节点;
- 校验失败后回退到哪个节点;
- 是否需要并行检索多个组件示例;
- 修复循环最多执行几次。
我的最终方案是以LangGraph为编排骨架,同时让LangChain的文档加载器、文本拆分器、向量存储接口作为节点内部的基础组件。两个不是对立关系,LangGraph内部完全可以拿LangChain的组件当积木。
2.2 为什么坚决不用在线API
原型的第一个版本用的是商业API,效果确实很好,但遇到了三件让我下决心换掉的事情:
- 公司内部的前端代码属于研发资产,不能直接发到第三方API做推理;
- 项目高峰期API费用肉眼可见地涨,按团队规模算是一笔不小的固定成本;
- Agent的上下文中要注入组件库代码和业务示例,这些属于内部信息,不适合出内网。
2.3 开源模型具体怎么选
我在自己搭的一套“前端生成测试集”上跑了四类主流开源模型,包含20个生成任务(5个整页、8个区块、7个组件封装),从代码可运行率、风格匹配度、中文理解准确率三个维度打分(满分10分),结果见下表:
| 模型 | 参数量 | 可运行率 | 风格匹配度 | 中文理解 | 显存占用(8bit量化) | 备注 |
|---|---|---|---|---|---|---|
| Qwen2.5-72B-Instruct | 72B | 9.0 | 8.8 | 9.4 | 约70GB | 综合最强,中文代码理解到位 |
| DeepSeek-V3 | 671B MoE | 9.2 | 8.5 | 9.0 | 不可单卡部署 | 效果极佳但部署门槛太高 |
| Qwen2.5-Coder-32B-Instruct | 32B | 8.8 | 8.2 | 8.8 | 约34GB | 代码能力与72B接近,性价比之选 |
| Llama-3.1-8B-Instruct | 8B | 6.8 | 6.0 | 6.5 | 约10GB | 只能做简单组件,复杂逻辑扛不住 |
我自己最终选了Qwen2.5-Coder-32B-Instruct作为主力生成模型,原因很现实:单卡A100 40GB刚好能放下8bit量化版本,推理速度和效果之间比较平衡。用API做初筛时能力强的商用模型偶尔也会爆,但开源模型在复杂指令遵循上确实有差距,所以我在架构里设计了“校验修复循环”,用工程手段兜底模型能力上限。
提示:如果你没有70B级别显存的显卡,用32B加一层质量校验Agent,得到的结果不一定比裸调72B差,因为首遍生成不完美,后面修复环节能拽回来。
3. 系统整体架构与Agent工作流设计
整个系统我划分成四层:接入层、编排层、工具层、模型层。分层的目的只有一个:每层都能单独替换,不让技术债连坐。
3.1 四层架构横向拆解
| 层级 | 核心组件 | 职责说明 |
|---|---|---|
| 接入层 | REST API + WebSocket | 接收前端开发工具的请求,支持流式返回生成进度 |
| 编排层 | LangGraph StateGraph | 维护Agent状态机,串联“解析-检索-生成-校验-修复”五个节点 |
| 工具层 | 组件库索引、代码示例库、ESLint、编译沙箱 | 为Agent提供外部能力:向量检索、代码检查、执行验证 |
| 模型层 | vLLM + Qwen2.5-Coder-32B | 负责自然语言理解、代码生成、代码修复 |
3.2 Agent状态机:不是一次对话,而是五段流水线
工作流是核心设计。我把Agent内部定义成一条“流水线”而不是单个Prompt调一次,原因是在测试中我发现一次性让模型输出完整可运行页面,成功率只有62%。而把它拆成多段,每段聚焦一个子任务,再引入校验与修复循环,最终成功率能拉高到88%以上。
五个核心节点设计如下:
- 需求解析节点(RequirementParser):输入用户原始描述,输出结构化的
PageSpec(页面规格),包含区块清单、交互需求、数据需求、布局约束。这一步用模型的工具调用能力,强制返回JSON。 - 方案规划节点(SolutionPlanner):根据
PageSpec拆解生成步骤,决定需要检索哪些组件示例、生成顺序是什么。这个节点会输出一个Plan对象。 - 检索通知节点(ContextRetriever):根据
Plan从向量库检索组件库文档和相似项目代码片段,拼装成生成代码所需的上下文。 - 代码生成节点(CodeGenerator):这里的Prompt不是“帮我写一个页面”,而是包含系统约束、组件规范、检索示例、区块划分的结构化Prompt。
- 校验修复节点(ValidatorRepairer):对生成结果执行语法检查、编译检查、限制检查,如果失败则生成错误信息并回到第4步重试,最多重试3次。
用LangGraph实现时,状态图的核心结构是这样定义的(简化版本):
python复制from langgraph.graph import StateGraph, END
class AgentState(TypedDict):
user_input: str
page_spec: dict
plan: list
contexts: list
generated_code: str
valid_result: dict
retry_count: int
graph = StateGraph(AgentState)
graph.add_node("parse", parse_requirement_node)
graph.add_node("plan", scheme_planning_node)
graph.add_node("retrieve", context_retrieval_node)
graph.add_node("generate", code_generation_node)
graph.add_node("validate", validation_repair_node)
graph.set_entry_point("parse")
graph.add_edge("parse", "plan")
graph.add_edge("plan", "retrieve")
graph.add_edge("retrieve", "generate")
graph.add_edge("generate", "validate")
def should_repair(state: AgentState):
if not state["valid_result"]["is_ok"] and state["retry_count"] < 3:
return "generate"
return END
graph.add_conditional_edges("validate", should_repair)
这段代码的核心是should_repair这个条件路由,它决定了生成失败的代码会回到generate节点带上错误信息重新生成,而不是直接以失败收场。
3.3 状态管理与记忆设计
前端内容创作有很强的“上下文关联性”:用户先让你生成一个列表页,再让你给这个页面加导出功能。这两个请求不能孤立处理。
我用三段式记忆:
- 短期记忆(对话级):利用LangChain的
ConversationBufferMemory,保存当前会话最近6轮交互摘要,存在Redis里,超时清除。 - 中期记忆(项目级):把当前项目的路由、已有组件列表、package.json依赖信息做成一个
project_meta.json,每次请求自动读取注入到上下文。 - 长期记忆(用户级):记录用户对不同方案的偏好,比如“上次给表单加了label宽度统一配置”,这些被提炼成偏好条目,存在向量库里备查。
4. 核心模块的工程化落地细节
架构是骨架,这一章是血肉。我把每个关键模块的设计逻辑和实现要点全部展开说。
4.1 为什么不让模型直接生成Vue代码,而是先产出一份中间DSL
这是我整个架构里做过的最有价值的决定之一。最开始我让模型直接生成Vue SFC,结果输出偶尔会出现<script>标签包裹了模板内容、或者在template里写了v-for但忘了绑定:key,直接跑会报错,修起来也麻烦。
后来我引入了一层中间DSL——FrontendSpec DSL,一种结构化的JSON schema,用来描述页面结构,而不是直接输出代码。模型的任务是先生成这个DSL,再由我的编译器模板把它渲染成Vue/React代码。
一个典型DSL实例长这样:
json复制{
"page": {
"name": "UserListPage",
"layout": "container",
"blocks": [
{
"type": "SearchBar",
"fields": [
{ "name": "keyword", "label": "关键词", "component": "Input", "placeholder": "请输入用户名" },
{ "name": "status", "label": "状态", "component": "Select", "options": ["启用", "禁用"] }
]
},
{
"type": "DataTable",
"columns": [
{ "title": "用户ID", "dataIndex": "id" },
{ "title": "用户名", "dataIndex": "name" },
{ "title": "状态", "dataIndex": "status" }
],
"pagination": true
}
],
"api": {
"list": "/api/user/list",
"method": "GET"
}
}
}
为什么这样设计?因为JSON比代码更容易做结构性校验。我可以写校验器检查: blocks里的type是否在组件库注册表里、fields是否有重复的name、api配置是否合法。结构合法了,渲染出来的代码不会出现低级错误,大幅降低了后续修复循环的压力。
这个路由是关键转折点——从让模型直接写代码,变成让模型做结构化决策,然后我用确定性逻辑翻译成代码。
4.2 意图解析模块:函数调用是开源模型的刚需
大多数开源模型对“输出JSON”这件事的执行力比商业模型弱,但我发现如果使用模型自带的工具调用能力(Function Calling),而不是在Prompt里说“请输出JSON”,成功率能提升30%以上。
Qwen2.5系列对Function Calling支持比较完善。我的实现是用模型的tools参数定义好parse_page_spec工具,然后把用户需求传进去,要求模型输出这个工具的参数:
python复制from langchain_core.pydantic_v1 import BaseModel, Field
from langchain_openai import ChatOpenAI
class PageSpec(BaseModel):
title: str = Field(description="页面标题")
blocks: list[str] = Field(description="页面包含的功能区块类型列表,如search_bar、data_table")
interactions: list[str] = Field(description="交互需求,如行点击、批量删除")
api_requirements: list[str] = Field(description="数据接口描述,如需要用户列表接口")
llm = ChatOpenAI(
model="qwen2.5-coder-32b-instruct",
base_url="http://localhost:8000/v1",
api_key="EMPTY"
)
model_with_tool = llm.bind_tools([PageSpec])
resp = model_with_tool.invoke("生成一个用户列表页面,要有搜索、筛选、分页,操作列能编辑和禁用")
然后解析resp.tool_calls里的参数,得到结构化PageSpec对象。核心技巧:这个PageSpec定义要足够粗,不要想一步解析出所有字段,后面SolutionPlanner节点再细化。
4.3 组件检索与示例注入:RAG在这个场景里是怎么做的
接下来是组件的检索与注入。团队已有的组件库、代码仓库、历史页面是积累最多的知识资产,这些资产必须得到充分利用。
我采用的方案是:先建立组件文档索引 + 历史代码示例索引,再通过语义检索找出当前任务最相关的部分。
组件库索引的处理方式,把每个组件的.vue文件中提取的信息结构化:
- 组件名和功能描述(从注释或单独写的描述文件中读取)
- Props和
emit事件 - 代码片段(默认不注入,只在明确需要完整示例时注入)
- 使用示例(从Storybook或文档Demo里挖)
历史代码示例索引的构建方式,拿过去的真实页面代码预处理后存储:
python复制from langchain_community.vectorstores import FAISS
from langchain_community.embeddings import HuggingFaceEmbeddings
embeddings = HuggingFaceEmbeddings(model_name="BAAI/bge-large-zh-v1.5")
def build_component_index():
records = load_team_component_metadata() # 组件元信息
docs = []
for r in records:
doc = (
f"组件名: {r['name']} "
f"功能: {r['description']} "
f"props: {r['props']} "
f"events: {r['events']}"
)
docs.append(doc)
return FAISS.from_texts(docs, embeddings)
检索时配合混合策略:先关键词命中(组件名精确匹配),再向量语义召回,用RRF(Reciprocal Rank Fusion)合并排序。最终取前3条组件文档、前2条历史示例,组成上下文。
4.4 代码生成节点的Prompt是怎么组织的
代码生成节点的Prompt是整个系统里最不敢含糊的部分。我把Prompt固定成四段结构:
- 系统约束段:固定说明——你是前端开发助手,必须使用团队组件库(组件清单列表)、保持风格规范、遵循DSL schema。
- 项目上下文段:动态注入
project_meta.json、当前页面路由、允许使用的依赖。 - 检索结果段:注入组件文档、相似代码示例,并对示例修剪行数防止超上下文。
- 任务指令段:本次要生成的具体DSL、上次校验错误信息(如果有)。
生成节点实际执行时看起来是这样的(伪代码):
python复制system_prompt = f"""
你是团队的前端开发助手。你的工作是根据用户需求生成结构化页面描述DSL。
规则:
1. 只能使用组件库中已有的组件:{component_list}
2. 必须严格遵循给定DSL的JSON schema
3. 布局必须符合团队设计规范:间距8px倍数、主色#1677ff、圆角4px
4. 数据交互必须使用mockResponse字段描述,不要假设接口地址
"""
context_prompt = f"""
以下是与你需求相关的组件文档和相似页面示例,重点参考其中组件用法和结构:
{retrieve_context}
注意:参考时只借鉴组件类型和属性写法,不要直接复制示例中的业务文案和接口地址。
"""
task_prompt = f"""
用户需求:{user_input}
解析得到的页面规格:{page_spec_json}
方案计划:{plan_json}
当前重试次数:{retry_count}
最近一次校验错误:{last_error}
请生成完整、合法的页面描述DSL JSON,不要包含任何其他解释性文字。
"""
我特别强调一点:final那句“不要包含任何其他解释性文字”不是玄学,而是我在几十次测试后总结出的有效约束。 开源模型在生成JSON时非常喜欢输出“好的,这是为你生成的代码:”这类前缀,导致解析直接失败。加上这句之后,格式错误率下降了不少。
4.5 校验修复循环:Agent自愈能力的地基
校验修复是把“能生成”推向“能用”的关键。我在validate节点里实现了三级检查:
- 结构校验:用
json.loads解析模型输出,校验是否符合定义的DSL Schema,检查组件是否在注册表。 - 代码编译校验:将DSL渲染成Vue SFC后,写入临时目录,用ESLint和
vite-plugin-checker做语法与类型检查。 - 运行校验:这个更强,把渲染出来的代码放进沙箱环境,启动轻量级Vite编译,确认页面能真的跑起来不出红屏。
每一层失败都会生成一条描述清楚的错误信息。以“组件未注册”为例:
code复制[结构校验失败] blocks[1].type "UserTable" 不在组件库注册表中。
可选组件:SearchBar, DataTable, ActionButton, StatusTag, PaginationWrapper
这条错误文本会拼进下一轮task_prompt的last_error字段。模型看到这个提示后,大多能自主把组件名修正为正确值或加上import语句。
我在做评测时发现,校验修复给整体成功率带来的提升大约是:首遍生成成功率62%,一轮修复后提升到79%,二轮修复后到86%左右,三轮之后再提升就非常有限了。所以把重试次数设为3是投入产出比最高的设定。
5. 模型部署与推理参数调优:让开源模型跑出理想效果
本方案中模型部署和推理参数的决定,往往直接决定方案能不能落地成真。我在这一块花的时间不比业务逻辑少。
5.1 推理框架:vLLM是首选,但不是唯一选项
我先用Ollama做本地快速验证,但它对高并发和前缀缓存的支持不够强。生产环境我换到了vLLM,核心原因是它的PagedAttention能显著提升长Prompt场景下的显存利用率。前端生成场景的Prompt动不动就4000-6000个token,vLLM的优势非常明显。
启动命令示例:
bash复制python -m vllm.entrypoints.openai.api_server \
--model Qwen/Qwen2.5-Coder-32B-Instruct \
--quantization gptq \
--dtype float16 \
--max-model-len 32768 \
--gpu-memory-utilization 0.92 \
--trust-remote-code \
--served-model-name qwen2.5-coder-32b-instruct \
--port 8000
served-model-name这个参数建议设置成短名字,方便后面LangChain的model字段直接匹配。
5.2 量化与显存的平衡:Q8还是AWQ
模型量化是在显存和效果中进行权衡。Qwen2.5-Coder-32B在全精度fp16下需要大约65GB显存,一张80GB的A800/H100才有戏,但考虑到成本太不友好。我分别测了两种量化:
- GPTQ 8bit:显存占用约35GB,效果下降很小,基本可以忽略;
- GPTQ 4bit / AWQ:显存占用约19GB,但在复杂多步代码生成的场景中出现过变量名错乱和结构丢字段的问题。
最终我选的是8bit GPTQ + 32K上下文长度,一张A100 40GB刚好能跑。如果你手头只有24GB的卡,建议别硬上32B,选14B模型更稳妥——稳定输出优于偶尔惊艳。
5.3 推理参数:一档被很多人忽略的关键配置
LangChain调用模型时可以传model_kwargs,这里的参数直接影响代码质量。我实测后的推荐值:
| 参数 | 推荐值 | 原因 |
|---|---|---|
| temperature | 0.2 | 代码生成重确定性,太低没变化,太高就容易自由发挥 |
| top_p | 0.9 | 与temperature配合,保持少量多样性但不失控 |
| max_tokens | 8192 | 32B模型生成长页面需要长输出,设太短会截断半个标签 |
| frequency_penalty | 0.3 | 抑制重复生成同一行代码的问题 |
| stop | `["```", "< | im_end |
关于stop参数我再多说一句。开源模型在训练时混入了大量Markdown语料,所以即使我明确说了“不要包含解释文字”,它还是会在输出里包裹首尾的代码围栏。在vLLM中把```加入stop序列可以物理阻断这个行为,比在Prompt里说一百遍都管用。
5.4 上下文窗口的艺术:不是越长越好
Qwen2.5-Coder支持32K上下文,但我不建议每次请求都把大量组件文档塞进去。原因有两点:一是上下文越长,推理时延越长;二是开源模型容易在长上下文中被无关信息带偏,反而忽略真正的核心指令。
我的做法是只保留必要的上下文:
- 系统约束 + 项目元信息:约800 token;
- 检索到的组件文档和示例(严格修剪行数):约1500 token;
- 用户输入 + 页面规格 + 计划:约1000 token;
- 上轮错误信息:约300 token。
总输入窗口控制在4000 token左右,既保证信息密度,又给模型留足输出空间。这个约束需要长期调试,如果你发现模型经常“跑题”,看看是不是把太多无关文档塞进去了。
6. 实测中踩过的三个大坑:排查链路完整复盘
这一章写的都是真实踩坑记录,希望大家看完少走弯路。
6.1 大坑一:模型输出被Markdown代码块包裹导致编译全挂
现象是生成节点明明返回了完整DSL,但json.loads一直抛异常。我一开以为是模型指令遵循不够。后来把原始输出打印出来才发现,它是这样的:
json复制好的,这是为你生成的页面DSL:
```json
{
"page": {
...
}
}
code复制
模型把整个JSON包在Markdown的json代码围栏里,还带了一行问候语。
**排查过程**:
1. 第一步,我怀疑是模型指令遵循能力不足。第4.4节我已经加了“不要包含解释”。没啥用,它照吐。
2. 第二步,尝试用LangChain的`output_parser`里的`JsonOutputFunctionsParser`,能缓解但偶尔还是会漏。
3. 第三步,手动在代码里捕获异常后剥离Markdown围栏,用正则清理。治标不治本。
4. 最后,在vLLM启动命令中加入`stop`参数。把```物理阻断后,问题基本绝迹。
**排查结论**:Prompt只能“说服”模型,stop参数才能“阻止”模型。做Agent时,能用工程手段物理约束的地方,不要全靠模型自觉。
### 6.2 大坑二:模型自创组件名,根本不看组件库清单
现象是生成的页面里出现一堆不存在的组件,比如`<UserCardGrid>`、`<AdvancedTable>`。排查链路:
1. **怀疑向量检索失效**:组件文档没有召回到上下文中。断点验证后发现上下文里确实有组件清单,模型就是没看。
2. **拆解定位**:给一个只含组件清单、不掺业务描述的测试Prompt,模型能正确用组件名。说明问题出在“同时处理大量业务需求和组件清单”时,模型注意力被需求侧带走了。
3. **解耦任务**:把原来的“一次生成整个DSL”拆成“先生成区块清单,再逐个区块生成DSL”,每个区块生成时单独注入与该区块相关的组件清单和示例。组件名命中率瞬间提升。
**最终方案**:明知模型在长指令下注意力会飘,那就把任务切小。架构里`SolutionPlanner`节点的价值就在这里——它负责把大任务切成小步骤,每一步的信息载荷控制在模型能稳定处理的范围内。
### 6.3 大坑三:校验修复循环反复横跳,永远改不对
现象是某个页面生成后组件未注册,修复循环第一轮把组件名修对了,但把布局间距改了;第二轮间距改回来了,但把API字段弄错了。修复循环变成了“按下葫芦浮起瓢”。
**排查链路**:
1. 把修复循环的完整日志打出来,逐轮对比diff。发现前一轮的错误确实修好了,但引入了新错误。
2. 再检查Prompt。原来的修复Prompt只有“上次错误:xxx”,模型并不知道自己生成过什么,所以每次都是全量重写,自然容易整体崩盘。
3. 优化修复Prompt,改动策略:把“全量重新生成”改成“在上一版代码基础上打补丁”——原始代码原封不动提供,标注需要修改的位置(在错误信息中给出行号和期望值),要求模型最小化修改。
4. 这个改动让第二轮修复的成功率明显上升,副作用是单轮耗时增加了,但换来的是更稳定的收敛。
**教训**:Agent的修复循环不是“让模型重新做一遍”。修复的前提是保留现场、缩小范围、精确打击。这和人改Bug一个道理。
## 7. 实测效果评估与后续演进计划
方案落地后我跑了一个相对完整的评测,结果整理如下。
### 7.1 测试集设计与测评结果
测试集包含30个前端生成任务,分为5个整页、15个区块、10个组件封装,覆盖了表单、表格、详情、弹窗、布局、图表六个类型。每类任务重点关注的指标不同:
| 任务类型 | 指标 | 基线(单次调用) | Agent方案 |
|---|---|---|---|
| 整页生成 | 可运行率 | 62% | 88% |
| 区块开发 | 功能正确率 | 78% | 94% |
| 组件封装 | 一次性通过率 | 55% | 82% |
平均生成耗时:32B模型 + 8bit量化在A100 40GB上,单轮生成约8-12秒,一轮修复后总时长控制在20秒内,对一个交互式Agent来说属于可接受范围。
### 7.2 开源模型方案的上限与瓶颈
一个不得不承认的现状:32B级别开源模型在处理特别复杂的多条件组合需求时,稳定性确实不如顶尖商用模型。具体表现是检索相关组件时偶尔会有遗漏、在逻辑分支特别多的时候忘掉前提条件。这也是我后来考虑两条演进路径的原因。
**路径一:针对自建场景的微调**。收集团队的代码Review数据、修Bug记录、标准化历史页面,做成指令微调数据集。在对Qwen2.5-Coder-32B做LoRA微调后,预计能把“团队风格一致性”指标再拉高5-8个点。
**路径二:多Agent协同评审**。目前是一个生成Agent加一个校验Agent。下一步计划拆出专门的“代码Review Agent”和“视觉走查Agent”,生成完成后先自查一遍,再交付,把人工审查时间继续往下压。
### 7.3 对整个方案的自我复盘
这个方案最核心的收获,是把Agent从“一个能写代码的聊天框”变成了“一条带质量关卡的内容生产线”。我总结出三句话,也是我会在下一个Agent项目里继续坚持的原则:
- **能用工程确定性解决的问题,不要交给模型概率性解决。** 比如DSL结构校验、代码编译检查,这些环节用的是传统工具,不是模型。
- **模型只做它最擅长的事:把自然语言意图翻译成结构化方案。** 它生成的不是最终代码,而是中间表示,剩下的路由是确定性代码。
- **所有模型的不完美都要用系统设计来兜底。** 校验修复循环、stop参数、分层任务切割——每个方案里的“工程补丁”,加起来才是Agent真正能用的原因。
最后分享一个实用的小技巧,在搭建类似Agent时,如果发现某个环节始终做不对,先别怀疑模型能力或者急着换更大的模型。把这一步单独拎出来跑一个最小测试,看它到底是在Prompt引导、上下文组织还是输出解析哪一环出了问题。我的经验是,**大多数看起来像模型智商不够的问题,最后定位完都是工程细节不到位**——可能是忘了加stop标记,可能是上下文里混了多余信息,也可能是任务切得不够小。把这个排查顺序印在脑子里,做Agent会顺手很多。
