用华为云 CodeArts Agent 从零搭建 AI Chat Studio:一个 ChatGPT 风格的网页对话助手
用华为云 CodeArts Agent 从零搭建 AI Chat Studio:一个 ChatGPT 风格的网页对话助手
本文记录了如何使用华为云 CodeArts Agent(码道 ACP)从零开始,在沙箱环境中搭建一个完整的 AI 对话 Web 应用——AI Chat Studio。项目已发布至华为云开发者作品展览馆,代码开源在 GitCode。
一、项目简介
AI Chat Studio 是一个网页版 AI 对话助手,连接 9router API,支持多模型切换与流式输出。界面采用 ChatGPT 风格布局,左侧会话列表 + 右侧对话区,简洁现代。
核心功能
| 功能 | 说明 |
|---|---|
| 多轮对话 | 完整上下文管理,支持连续对话 |
| 模型选择 | 下拉框切换不同 AI 模型,支持从 API 自动拉取模型列表 |
| 模型配置 | 可自定义 API 端点、API Key、模型列表管理(增删改查) |
| 流式输出 | SSE 流式传输,逐字打字效果 |
| Markdown 渲染 | 代码语法高亮 + 一键复制按钮 |
| 历史会话 | localStorage 持久化,左侧会话列表管理(新建/切换/删除) |
| 主题切换 | 深色/浅色双主题,默认深色,偏好持久化 |
二、技术架构
技术栈选型
整个项目刻意保持了"轻量"的定位——不引入 React/Vue 等框架,用原生 HTML + CSS + JavaScript 实现前端,后端用 Node.js + Express 做一层 API 代理。
为什么这样选?
- 无框架前端:对于一个对话界面,原生 JS 完全够用,省去了构建工具链的复杂度,CDN 加载 marked.js / highlight.js / DOMPurify 三个库即可。
- Express 代理层:浏览器直接调用 AI API 会遇到跨域问题,且 API Key 不应暴露在前端。用 Express 做一层代理,既解决跨域,又保护密钥。
- SSE 流式传输:AI 生成内容是逐 token 返回的,用 Server-Sent Events 实现流式转发,前端逐字渲染,用户体验远优于"等完再显示"。
架构图
┌─────────────────────────────────────────────┐
│ 浏览器 │
│ ┌─────────┐ ┌──────────────────────────┐ │
│ │ 会话列表 │ │ 对话区域 │ │
│ │ (左侧) │ │ Markdown + 代码高亮 │ │
│ │ │ │ 流式逐字渲染 │ │
│ └─────────┘ └──────────────────────────┘ │
│ │ │ │
│ localStorage SSE EventSource │
└─────────┼────────────────────┼───────────────┘
│ │
│ ┌───────┴───────┐
│ │ Express 代理 │
│ │ (Node.js) │
│ │ /api/chat │
│ │ /api/models │
│ └───────┬───────┘
│ │
│ ┌───────┴───────┐
│ │ 9router API │
│ │ (上游 AI) │
│ └───────────────┘
项目结构
ai-chat-studio/
├── server.js # Express 服务器 + API 代理
├── package.json # 依赖配置
└── public/
├── index.html # 主页面(CDN 加载第三方库)
├── style.css # 样式(深色/浅色主题 CSS 变量)
└── app.js # 前端逻辑(对话管理 + 设置面板)
三、核心实现解析
3.1 SSE 流式代理(后端)
后端最核心的部分是 /api/chat 接口——它接收前端的 POST 请求,转发给上游 9router API,然后把上游的流式响应逐块转发回前端。
app.post('/api/chat', async (req, res) => {
const upstream = await fetch(`${apiBaseUrl}/v1/chat/completions`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${apiKey}`
},
body: JSON.stringify(req.body)
});
// 设置 SSE 响应头
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive');
// 逐行读取上游响应并转发
const reader = upstream.body.getReader();
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader.read();
if (done) break;
res.write(decoder.decode(value));
}
res.end();
});
关键点:Express 代理层不做任何缓冲,上游返回一块就立即写一块到前端响应,保证"逐字打字"效果的真实性。
3.2 前端流式接收与渲染
前端用 fetch + ReadableStream 接收 SSE 数据(而非 EventSource,因为需要 POST 请求):
const response = await fetch('/api/chat', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ model, messages, stream: true })
});
const reader = response.body.getReader();
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader.read();
if (done) break;
const chunk = decoder.decode(value);
// 解析 SSE data: 行,提取 content
const lines = chunk.split('\n');
for (const line of lines) {
if (line.startsWith('data: ')) {
const data = line.slice(6);
if (data === '[DONE]') return;
const json = JSON.parse(data);
const content = json.choices[0]?.delta?.content;
if (content) {
assistantMessage += content;
renderMarkdown(assistantMessage); // 实时渲染
}
}
}
}
3.3 Markdown 渲染与代码高亮
用 CDN 加载三个库,无需构建步骤:
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/highlight.js@11/highlight.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/dompurify@3/dist/purify.min.js"></script>
渲染流程:marked.parse(text) → DOMPurify.sanitize(html) → 插入 DOM → hljs.highlightAll()。
代码块还加了"一键复制"按钮——监听 click 事件,navigator.clipboard.writeText() 写入剪贴板。
3.4 模型配置面板
设置数据存在 localStorage,结构如下:
{
"apiBaseUrl": "https://api.9router.com",
"apiKey": "sk-xxx",
"defaultModel": "gpt-4o-mini",
"models": [
{ "id": "gpt-4o", "name": "GPT-4o" },
{ "id": "gpt-4o-mini", "name": "GPT-4o Mini" }
]
}
支持从 API 自动拉取模型列表(调用 /api/models),也支持手动增删改查。设置面板用 overlay + modal 实现,点击设置按钮弹出,点击遮罩层关闭。
3.5 深色/浅色主题
用 CSS 变量实现主题切换,切换时只需修改 document.documentElement 的属性:
:root {
--bg-primary: #1a1a2e;
--text-primary: #e0e0e0;
/* ... */
}
:root[data-theme="light"] {
--bg-primary: #ffffff;
--text-primary: #1a1a1a;
/* ... */
}
主题偏好同样存在 localStorage,刷新页面后自动恢复。
四、开发过程:CodeArts Agent 实践
这个项目完全在华为云 CodeArts 的沙箱环境中开发,使用 ACP(Agent Coding Protocol)协议与 Agent 交互。
开发流程
- 创建项目:通过 CodeArts acpx 会话创建
ai-chat-studio项目,初始化package.json和 Express 依赖。 - 编写后端:Agent 根据"ChatGPT 风格对话助手"的需求描述,生成
server.js,包含 SSE 代理和模型列表代理两个接口。 - 编写前端:依次生成
index.html(页面结构 + CDN 引入)、style.css(深色主题 + 响应式布局)、app.js(对话逻辑 + Markdown 渲染 + 历史管理)。 - 功能迭代:在初版基础上增加模型配置面板——设置弹窗、localStorage 持久化、模型列表管理。
- 调试运行:在沙箱内
node server.js启动,通过 DevBridge 隧道从外部访问验证。
CodeArts Agent 的优势
- 沙箱隔离:开发环境完全隔离,不污染本地系统,Node.js、npm 等工具链开箱即用。
- Agent 协作:用自然语言描述需求,Agent 生成代码,人工审查后采纳。比纯手写快,比纯 AI 生成可靠(有沙箱即时验证)。
- 隧道预览:DevBridge 隧道一键暴露沙箱端口,外部浏览器直接访问预览,无需部署。
五、快速开始
# 克隆仓库
git clone https://gitcode.com/sdf2342/ai-chat-studio.git
cd ai-chat-studio
# 安装依赖
npm install
# 配置环境变量(可选,也可在界面设置面板中配置)
export APIBASE_URL=https://api.9router.com
export API_KEY=your-api-key
# 启动服务
node server.js
默认运行在 http://localhost:8080。
也可以不设环境变量,启动后在界面右上角点击设置按钮,在弹出的配置面板中填写 API 地址和密钥。
六、项目发布
项目已发布至华为云开发者作品展览馆:
- 作品链接:https://gallery.developer.huaweicloud.com/gallery/005facc814400000
- 代码仓库:https://gitcode.com/sdf2342/ai-chat-studio
- 投稿活动:闯关活动
发布流程使用了 huawei-cloud-publish-work-to-gallery 技能,自动完成了封面截图、架构图生成、详情打包、门禁检查和上传发布全流程。
七、总结与展望
AI Chat Studio 是一个"麻雀虽小五脏俱全"的项目——用最简的技术栈实现了完整的 AI 对话体验。它的价值不在于功能多强大,而在于:
- 可读性:没有框架 abstraction,每一行代码都直接对应一个可见的行为,适合学习。
- 可部署性:一个
node server.js就能跑,不需要 Docker、不需要构建工具。 - 可扩展性:加一个数据库就是多用户版,加一个 RAG 就是知识库问答版。
后续可以考虑:
- 接入更多 AI 平台(华为云 ModelArts / MaaS)
- 增加文件上传与对话
- 用 RAG 实现知识库问答
- 部署到华为云 CCE 或 FunctionGraph
相关链接:
- 点赞
- 收藏
- 关注作者
评论(0)