最近有幸参与了公司内部的一个 AI 项目的开发,想分享一下过程中遇到的问题以及收获的经验
项目背景
在 问卷 场景接入 AI ,支持
AI创建问卷。AI生成统计。
当时调研发现 腾讯问卷 已经接入了 AI 相关的能力,想直接接入节省开发成本。但奈何费用太贵了,所以决定参考它的交互来自研实现。
核心功能实现思路
AI 生成问卷
我的实现思路是让 AI 生成定义好的 json 来渲染表单。
具体流程如下:
---
config:
layout: dagre
---
flowchart LR
Start(["开始"]) --> Define["定义 JSON 字段要求说明"]
Define --> Prompt["作为提示词发送给 AI"]
Prompt --> Gen["AI 根据要求返回指定格式 JSON"]
Gen --> Validate{"使用 Zod 校验"}
Validate -- 校验失败 --> Retry["修复提示词/重试"]
Retry --> Prompt
Validate -- 校验通过 --> Loop["遍历 JSON 数据"]
Loop --> Render["生成并渲染相应的表单组件"]
Render --> End(["完成"])
style Define fill:#f9f,stroke:#333,stroke-width:2px
style Gen fill:#bbf,stroke:#333,stroke-width:2px
style Validate fill:#fb1,stroke:#333,stroke-width:2px
style Render fill:#bfb,stroke:#333,stroke-width:2px
AI 生成统计结果
生成统计结果有以下要求:
markdown流式渲染- 支持各类图表(饼图、折线图等)
关于 markdown 流式渲染,我找到了以下优秀的开源解决方案:
| 项目名称 | 开发团队 | 支持框架 |
|---|---|---|
| streamdown | Vercel | React |
| @ant-design/x-markdowns | Ant Design | React |
| markstream-vue | 社区开发者 (Simon-He95) | React, Vue |
我在项目中使用的是 streamdown,但是它对流式渲染类似 <custom-component></custom-component> 的自定义组件支持的并不好,只能自定义内置的一些 markdown 组件。
而 x-markdown 在设计时便考虑到了自定义组件的处理,会在解析 markdown 时为自定义组件添加流式处理相关的 props ,你可以方便的处理加载中的状态。如果你本来就使用了 ant design 作为组件库,并且有在 markdown 中渲染自定义组件的需求,首选 x-markdown
图表渲染我通过 mermaid 来实现,让 AI 来选择合适的图表展示统计结果。也可以通过自定义图表组件来实现,但是要将组件的 api 整理成文档发给 AI,阿里巴巴也开源了相关的图表渲染实现,可以作为参考。
问题和经验总结
多 AI 接入
我们在项目中接入了多个 AI 大模型:
- deepseek v3
- deepseek r1
- qwen3-max
- qwen3-plus
当模型多了以后,问题也随之而来:相同的提示词,有的模型可以完美输出结果,但是有的模型连提示词都不能正确理解。
为了解决这个问题,我们将提示词单独抽离出来在团队内部进行展示,征集团队的意见来改进优化提示词,甚至要针对不同的模型适配专有的提示词。
这让我更加深刻的体会到为什么需要提示词工程。
提示词设计
下面涉及到的代码和提示词只是辅助说明思路,实际项目中还需要根据业务场景进行调整
问卷系统中集成了很多表单项,如果将所有表单项的 api 一次性发给 AI 的话,上下文会很庞大并且有很多 AI 完全不需要的表单 api。
为了解决这个问题,我先用一个 map 来存储所有的表单项以及它的 api:
const apiMap = {
input: {
description: '组件介绍',
api: 'markdown api 描述'
},
radio: {
description: '组件介绍',
api: 'markdown api 描述'
},
checkbox: {
description: '组件介绍',
api: 'markdown api 描述'
},
// ... 省略其它组件
}
然后我把和 AI 的交互分为2步:
- 设计表单
- 生成表单
json
在设计表单阶段,我只需要将所有支持表单项的名称和描述发给 AI :
const prompt = `你是一个表单设计专家,请根据以下表单组件列表设计一个问卷表单
支持的表单组件列表:
${Object.entries(apiMap).map(([key, value]) => `- ${key}: ${value.description}`).join('\n')}
请输出一个包含表单内容和表单项的设计, 格式如下:
{
"formItems": [ "input", "radio", ... ] // 只包含表单项名称
"content": "问卷具体内容..." // 使用 markdown 格式描述问卷的具体内容
}
在生成表单 json 阶段,我会根据上一步 AI 返回的表单项名称,从 apiMap 中取出对应的 api 发给 AI :
const apis = formItems.map((item: string) => {
return `### ${item}\n\n${apiMap[item].api}`
}).join('\n\n')
const prompt = `你是一个表单生成专家,表单内容如下:
${content}
表单组件 API 如下:
${apis}
请根据表单内容和组件 API 生成一个符合要求的 JSON
`
这样就可以在上下文中去掉无关表单项的 api,极大的减少上下文占用问题。
日志
在真正投入使用的时候,渐渐出现了以下问题:
- 总听到有人说:"
AI生成的内容为什么和我的需求完全没有关联?怎么生成时间这么长?" - 同事和领导可能会质疑这是
AI的问题,还是你自己的代码逻辑有问题? - 提示词是通过各种复杂业务逻辑拼接出来的,比较难定位是提示词写的有问题,还是代码写的有问题。
为了应对以上问题,我们可以为 AI 接口请求添加日志,记录请求的提示词、耗时、响应内容等关键指标,帮助我们更好的排查问题。
结语
以上便是我在整个过程中的思考和总结。
在 AI 爆火的这段时间,我一直在积累相关的知识,但是却一直没有一个很好的实践机会。这个项目是一个很好的契机,帮我在真实项目中验证了自己的学习成果,希望也能对你有所帮助。