万悟平台二次开发实战:从代码修改到镜像打包、测试部署的全流程指南
本指南旨在为需要进行万悟平台(WanwuLite)二次开发的开发者提供一份从本地代码修改、Docker镜像构建、本地功能验证到最终部署发版的完整、一站式的操作手册。模型管理: 支持导入 LLM、Embedding 和 Rerank 模型,并进行模型推理。知识库: 支持创建知识库,进行知识管理,并提供向量检索、全文检索和混合检索等功能。工具广场: 提供精选推荐的 MCP server,并支持导入自定
文档目录与内容框架
一、 前言
1.1 本文目标
-
本指南旨在为需要进行万悟平台(WanwuLite)二次开发的开发者提供一份从本地代码修改、Docker镜像构建、本地功能验证到最终部署发版的完整、一站式的操作手册。
1.2 适用人群
-
对万悟平台进行定制化开发的软件工程师、运维工程师。
-
需要了解完整CI/CD流程的开发者。
-
对Docker、容器化部署感兴趣的初学者。
1.3 环境与前提准备
-
开发环境: Linux/Mac/Windows WSL2
-
内存建议:本地开发建议至少32G内存,尤其是Windows系统用户,避免因IDE、Docker等占用导致运行卡顿或异常。
-
必备工具:
-
Git: 用于代码版本管理。
-
Docker & Docker Compose: 用于镜像构建和容器化部署。
-
Go(万悟平台实际技术栈): 本地开发调试环境。
-
-
已有资源:
-
万悟平台源代码仓库访问权限。
-
基础的万悟平台使用知识。
-
二、代码结构
万悟的后端是采用Golang语言开发的,该项目代码结构采用了标准的Golang项目布局(Standard Go Project Layout),并结合了前后端开发所需的配置,万悟代码结构:

1. 核心目录结构
① /cmd:应用程序入口点,通常包含多个子目录,每个子目录对应一个可执行程序(如 main.go)。
② /internal:私有代码,仅限项目内部使用,不对外暴露。
③ /api :Protocol Buffers 生成的代码文件。
④ /pkg:公共库代码,可被其他项目导入(如数据库、日志、工具函数等)。
⑤ /proto:Protocol Buffers 定义文件。
⑥ /vendor:Go 依赖的本地副本(通过 go mod vendor 生成),用于离线依赖管理。
⑦ /web :前端相关代码(如静态文件、前端框架代码等)。
2. 配置与部署
① /configs:配置文件模板或默认配置(如 YAML、JSON 文件)。
② /bin:编译生成的可执行文件(通常会被 .gitignore 忽略)。
③ Docker 相关文件:docker-compose.yaml 和 docker-compose-develop.yaml :定义多容器服务的开发和生产配置。
④ 环境变量文件:.env、.env.image.amd64、.env.image.arm64:不同环境或架构的配置变量。
3. 依赖与文档
① go.mod 和 go.sum:Go 模块管理文件,明确项目依赖及其版本。
② /docs:项目文档(Swagger/OpenAPI 规范的文档 和 Go 代码生成的 API 文档)。
③ README.md 和 license:项目说明和许可证文件。
4. 其他文件
-
Dockerfile.backend 和 Dockerfile.frontend :分别用于构建后端和前端镜像。
-
Makefile:一个 Go 项目的构建、测试、文档生成和部署流程的工具。
-
Makefile.deploy:一个在开发中对于各服务的控制,便于测试的工具。
5. 核心目录详解
① /cmd
-
每个微服务对应一个子目录,如 app-service、assistant-service等
-
每个目录下包含main.go作为服务入口点
-
main.go负责初始化配置、日志、数据库连接,并启动服务
② /internal
-
按微服务划分目录,如 internal/app-service/
-
每个服务内部包含 client、config、server 等子包
-
server/grpc/目录包含gRPC服务实现
③ /api与 /proto
-
包含服务间通信的Protocol Buffers定义
-
按服务划分目录,如 api/proto/app-service
-
定义了服务接口和消息格式
④ /pkg
-
包含公共功能模块,如数据库客户端、日志工具、MinIO客户端等
-
这些包可被多个微服务引用
-
遵循Go的包管理最佳实践,只包含可被外部安全使用的代码
6. 核心业务微服务说明
① bff-service:api服务接入服务,业务串联
② iam-service:身份认证服务,多租户管理及访问权限管理
③ model-service:模型管理服务,处理模型的接入和管理
④ rag-service:文本问答服务
⑤ app-service:api key管理、安全护栏敏感词服务
⑥ assistant-service:智能体应用服务
⑦ mcp-service:MCP服务
⑧ operate-service:系统设置管理服务
⑨ knowledge-service:知识库服务
该项目是一个 基于 Go 的微服务系统,具备清晰的目录结构、自动化构建部署流程,便于新功能的扩展。符合Go语言的最佳实践,为后续的开发和维护奠定了良好基础。
三、 获取代码与本地开发
3.1 克隆代码仓库
-
示例命令:
git clone <repository_url> -
切换至正确的开发分支或Tag:
git checkout -b dev
3.2 项目结构简介

-
模型管理: 支持导入 LLM、Embedding 和 Rerank 模型,并进行模型推理。
-
知识库: 支持创建知识库,进行知识管理,并提供向量检索、全文检索和混合检索等功能。
-
工具广场: 提供精选推荐的 MCP server,并支持导入自定义 MCP 服务。
-
安全护栏: 支持创建和管理敏感词表,控制模型反馈结果的安全性。
-
文本问答: 支持创建和管理文本问答应用,并生成 API 接口。
-
工作流: 支持创建和管理工作流,并进行调试和发布。
-
智能体: 支持创建和管理智能体,并进行调试和发布。
-
应用广场: 提供应用体验和历史应用管理功能。
-
设置: 支持个人信息修改、组织管理、组织切换和平台配置等功能。
3.3 进行代码修改
-
我们在第三节会以一个简单的示例为例(如何增加一个平台配置权限)。
-
强调修改代码时要遵循的规范。
3.4 本地运行与调试
-
如何在本地启动依赖服务(如数据库、Redis)。
-
如何运行和调试主程序,确保修改在本地环境是有效的。
四、后端二次开发流程示例
-
二进制构建:通过
Makefile中的命令构建后端微服务(如bff-service、model-service)的二进制文件,输出至bin/amd64目录。
-
容器替换流程:停止原容器,使用开发镜像(如Golang基础镜像)挂载新二进制并重启服务,实现局部更新。
-
多服务协同更新:当接口变更涉及多个微服务时,需同步更新相关容器以保证前后端一致性。
接下来我们走一遍二次开发的流程:
4.1 配置权限
以权限功能为例,首先需要明确新功能模块的权限需求,以一级权限为例,我们增加了平台配置权限setting

4.2 创建文件
绑定我们在中间件初始化时定义的权限,并根据规范定义好路径、方法、处理函数
internal/bff-service/server/http/handler/router/v1/setting.go

4.3 注册新的方法
internal/bff-service/server/http/handler/router/v1/router.go

4.4 实现处理函数以及swagger API的定义
internal/bff-service/server/http/handler/v1/setting.go

@Tags:将此接口归类到 "setting" 分组
安全要求:需要 JWT 认证
请求/响应格式:JSON
参数说明:
-
接收一个
request.CustomTabConfig结构体作为请求体(如果是get请求则是query格式) -
成功时返回
response.Response格式的响应
路由:POST /custom/tab
到定义swagger这一步时需要和前端讨论定义的方法、路由、结构体对需求的可行性
使用 gin_util.Bind() 将请求体 JSON 绑定到 request.CustomTabConfig 结构体,接着从上下文中获取用户id和组织id,调用 service.UploadCustomTab() 服务层方法,最后使用 gin_util.Response() 统一封装返回
4.5 生成proto代码
根据需求定义proto中的消息格式和服务接口 make pb生成代码
proto/operate-service/operate-service.proto

4.6 service层的实现
service层的实现,调用grpc方法:
internal/bff-service/service/setting.go

4.7 定义数据库
在微服务层定义需要存储的数据库表结构 (internal/operate-service/client/orm/client.go中需要同步在db.AutoMigrate方法中添加数据库表的结构体)
internal/operate-service/client/model/system_custom.go

4.8 server层实现
微服务server层实现,也是实现grpc的方法
internal/operate-service/server/grpc/operate/system_custom.go

4.9 client层接口
IClient接口用于隔离业务逻辑层(如 gRPC 方法实现)与基础设施层(如数据库操作)
internal/operate-service/client/client.go

4.10 数据库操作的实现
internal/operate-service/client/orm/system_custom.go

4.11 注意事项
-
proto消息格式必须为每个字段分配唯一的数字标识符,通常递增。
-
swagger注解中如果是get请求需要是query。
-
数据库表定义时避免使用 SQL 关键字作为列名,必要时使用反引号或自定义列名。
-
在微服务client层中返回的错误需要用
toErrStatus方法返回,其中参数需要定义key,server层需要定义code,这些是在proto的err-code中定义的,主要用于后期如果需要做全局翻译,同步要更新wanwu-i18n.jsonl文件,并执行make i18n-jsonl -
如果是添加新的微服务,需要在
configs/middleware/mysql/initdb.d/init.sql中添加初始化数据库语句,并执行make run-mysql-worker
五、 前端二次开发流程示例
-
前端构建方式:可通过本地
npm run build构建,或从开发环境拉取打包后的前端包进行部署。 -
目录结构管理:构建生成的
dist目录需重命名为aibase,并放置于project/nginx/html下供Nginx容器使用,替换旧版本并更新Nginx内容。
5.1 进入web目录

5.2 安装依赖
根据情况安装依赖:npm install,如果已有可跳过
5.3 二次开发
根据需求更改前端代码,进行二次开发。
5.4 构建项目
修改完成后准备构建项目:npm run build
5.5 打包并部署测试
在wanwu/web下更改dist文件夹为aibse: mv dist aibase
压缩成aibase.tar.gz包: tar -zcvf aibase.tar.gz aibase
将压缩包移动至project/frontend下: mv aibase.tar.gz /wanwulite/project/frontend
在wanwu路径下执行解压命令: make -f Makefile.develop unzip-frontend-aibase
在wanwu路径下执行部署命令: make -f Makefile.develop update-frontend-aibase
六、 本地测试与验证
6.1 使用Docker Compose启动全套服务
-
启动命令:
amd64系统执行: docker compose --env-file .env --env-file .env.image.amd64 up -d arm64系统执行: docker compose --env-file .env --env-file .env.image.arm64 up -d -
查看日志,确认所有容器健康启动:
docker ps -a注意
mysql-worker与elasticsearch的EXIT状态是正常的。
6.2 构建服务镜像
-
以第四部分为例,我们在后端开发中修改了
bff-service与operate-service两个服务。现在对修改过的服务进行镜像构建。 -
执行命令(以amd64架构为例):
make build-bff-amd64 make build-operate-amd64此时在
/bin/amd64文件夹下会出现我们构建好的对应镜像文件
-
构建好后就可以启停服务进行镜像加载了,执行以下命令后即可:
make -f Makefile.develop stop-operate make -f Makefile.develop stop-bff make -f Makefile.develop run-operate make -f Makefile.develop run-bff
6.3 功能测试
-
对第三步中修改的功能进行测试。
-
可以根据需求通过Curl命令或Postman操作步骤,验证API返回是否符合预期。
6.4 问题排查
-
如果测试失败,如何查看容器日志:
docker logs -f <container_id> -
如何进入容器内部排查:
docker exec -it <container_id> /bin/bash
七、 构建Docker镜像
前后端及workflow文件夹目录结构要求:
wanwu-workflow文件夹与Dockerflie.frontend文件,需要与wanwu文件夹同级放置,如下图所示:
下面是Dockerflie.frontend文件的内容,如果无法获取相关代码,可以复制下面代码构建文件。
# --- 第一阶段:构建阶段aibase ---
FROM node:14 AS builder-aibase
WORKDIR /app
COPY wanwu/web .
ENV npm_config_registry=https://registry.npmmirror.com
ENV npm_config_unsafe_perm=true
RUN set -euo && npm install
RUN set -euo && npm run build
# --- 第二阶段:构建阶段workflow ---
FROM node:22 AS builder-workflow
WORKDIR /app
COPY wanwu-workflow .
ENV npm_config_registry=https://registry.npmmirror.com
RUN cd /app/frontend && npm install -g @microsoft/rush && rush install && rush update
RUN cd /app/frontend/apps/coze-studio && npm run build
# --- 第三阶段:运行阶段 ---
FROM nginx:1.27
COPY wanwu/configs/middleware/nginx/conf.d /etc/nginx/conf.d
COPY --from=builder-aibase /app/dist /usr/share/nginx/html/aibase
COPY --from=builder-workflow /app/frontend/apps/coze-studio/dist /usr/share/nginx/html/workflow
CMD ["nginx", "-g", "daemon off;"]
特别强调: 注意构建镜像的平台(.env文件中的版本参数与架构信息),避免在AMD64机器上构建出ARM64的镜像,反之亦然。

-
其中部分镜像打包先 ->后顺序:workflow -> backend -> frontend
7.1 workflow镜像构建命令
① 进入 /wanwu-workflow文件夹下切换到当前开发分支,git pull 拉取最新代码
② 进入 /wanwu-backend文件夹执行 make init ,生成新的vendor,然后返回上一层
③ 打包镜像:在Makefile中查找相关命令,执行:
make -f Makefile.wanwu docker-image-backend
7.2 后端镜像构建命令详解
① 切换到当前开发分支,git pull 拉取最新代码,执行 make init ,生成新的vendor。
② 查看.env文件版本并更新,执行:
vimdiff .env .env.bak
③ 打后端镜像,执行:
make docker-image-backend
7.3 前端镜像构建命令详解
-
注意:需要先进行workflow与后端发版,再根据后端打出的镜像号命名前端镜像!
-
流程示例:以21机器上wanwu与wanwu-workflow仓库中arm64版本的frontend为例
① 确认wanwu与wanwu-workflow切到开发分支,均git pull 拉取最新代码,执行 make init 之后。
② 查看.env文件版本并更新,执行:
vimdiff .env .env.bak
-
确认wanwu文件夹与wanwu-workflow文件夹在/a2pp文件夹下处于同一层级
-
打前端镜像,切换至/a2pp文件夹下,执行:
docker build -f Dockerfile.frontend -t wanwulite/frontend:Tag信息 .
7.4 rag镜像构建命令详解
① 在 /wanwu 文件夹下,切换到当前开发分支,git pull 拉取最新代码,执行 make init ,生成新的vendor。
② 查看.env文件版本并更新,执行:
vimdiff .env .env.bak
③ 打rag镜像,执行:
make docker-image-rag
7.5 agent镜像构建命令详解
① 在 /wanwu 文件夹下,切换到当前开发分支,git pull 拉取最新代码,执行 make init ,生成新的vendor。
② 查看.env文件版本并更新,执行:
vimdiff .env .env.bak
③ 打agent镜像,执行:
make docker-image-agent
7.6 callback镜像构建命令详解
① 在 /wanwu 文件夹下,切换到当前开发分支,git pull 拉取最新代码,执行 make init ,生成新的vendor。
② 查看.env文件版本并更新,执行:
vimdiff .env .env.bak
③ 打callback镜像,执行:
make docker-image-callback
7.7 base镜像构建命令详解
base镜像包含4种:workflow-base、rag-base、callback-base、agent-base(v0.3.0后已舍弃)
① workflow-base:
# 进入 workflow 目录
cd wanwu-workflow
# 切换到开发分支并更新代码
git checkout dev
git pull
# 更新环境变量版本(对比并更新.env文件)
cd wanwu-backend
vimdiff .env .env.bak
# 生成 vendor
cd wanwu-backend && make init && cd ..
# 构建 workflow-base 镜像(在 /a2pp/workflow-wanwu 目录下执行)
make -f Makefile.wanwu docker-image-backend-base
# 同步修改Dockerfile.wanwu-backend里面的基础依赖base镜像名称
vim Dockerfile.wanwu-backend
② callback-base:
# 切换到开发分支
git checkout dev
git pull
# 更新环境变量版本(对比并更新.env文件)
vimdiff .env .env.bak
# 构建callback-base镜像(在 /a2pp/wanwu 目录下执行)
make docker-image-callback-base
# 同步修改Dockerfile.callback里面的基础依赖base镜像名称
vim Dockerfile.callback
7.8 验证镜像
-
查看镜像是否构建成功:
docker images -
记得同样更改
.env.image.amd64和.env.image.arm64中前端、后端、workflow、rag、callback以及相关base镜像信息

7.9 注意事项
-
发版完成后,通过命令重启前端,执行:
make -f Makefile.develop stop-nginx
make -f Makefile.develop run-nginx
7.10 特殊情况
如果用户只需要更新前端镜像,而不修改后端镜像的话,镜像Tag可以自定义,或者还是与当前后端的对齐,版本号建议修改一下,以便与之前正式版本区别开。
八、打Tag与推送至镜像仓库
8.1 为镜像打上版本Tag
-
命令示例:
docker tag wanwulite-backend:custom your-registry.com/your-project/wanwulite-backend:v1.1.0-custom
8.2 登录私有镜像仓库
-
命令示例:
docker login your-registry.com
8.3 推送镜像
-
按照云上的推送镜像命令操作即可,命令示例:
docker push your-registry.com/your-project/wanwulite-backend:v1.1.0-custom
8.4 推送示例
示例:安全起见,以一个当前版本已经弃用的agentscope镜像从打tag到推送到阿里云镜像仓库为例
-
通过docker images 获取agentscope的镜像号

-
控制台登录阿里云,执行命令:
docker login --username=账户名称 crpi-6pxxxxxxxs8.cn-hangzhou.personal.cr.aliyuncs.com
-
对镜像添加tag信息,执行命令:
docker tag [镜像ID] crpi-6pxxxxxxxxs8.cn-hangzhou.personal.cr.aliyuncs.com/wanwulite/[镜像名称]:[Tag信息]-[架构]
例如: docker tag a127a417d5fa crpi-6pjxxxxxxxxexs8.cn-hangzhou.personal.cr.aliyuncs.com/wanwulite/agentscope:v0.1.0-9f2a8b2f-arm64添加好后,此时执行docker images会出现两个Tag不同的agentscope,如下图
注意它们的IMAGE ID和SIZE必须一模一样
-
推送带有架构信息TAG的镜像到阿里云,执行:
docker push crpi-6pxxxxxxxxs8.cn-hangzhou.personal.cr.aliyuncs.com/wanwulite/agentscope:[镜像版本号]
-
在arm64和amd64均推送对应架构信息的TAG到阿里云之后执行下面命令生成manifest
docker manifest create crpi-6pxxxxxxxxs8.cn-hangzhou.personal.cr.aliyuncs.com/wanwulite/agentscope:v0.1.0-9f2a8b2f \ crpi-6pxxxxxxxxs8.cn-hangzhou.personal.cr.aliyuncs.com/wanwulite/agentscope:v0.1.0-9f2a8b2f-arm64 \ crpi-6pxxxxxxxxs8.cn-hangzhou.personal.cr.aliyuncs.com/wanwulite/agentscope:v0.1.0-9f2a8b2f-amd64
-
执行成功后把新建的manifest推送到阿里云上
docker manifest push crpi-6pxxxxxxxxs8.cn-hangzhou.personal.cr.aliyuncs.com/wanwulite/agentscope:v0.1.0-9f2a8b2f此时阿里云上会出现一个多架构的agentscope镜像,如下图,它的镜像大小应与两种架构中的一个相同。

8.5 更新镜像信息
确认.env.image.amd64以及.env.image.arm64中的agentscope的Tag信息已更新并且完全相同,且不带架构信息。然后提merge请求
到开发分支
九、部署上线(发版)
9.1 部署环境准备
-
确保目标服务器已安装Docker。
-
确保服务器能访问私有镜像仓库。
9.2 更新部署配置
-
修改生产环境的
.env.image.amd64或.env.image.arm64文件中的镜像标签,将其指向第八步推送的新镜像。
9.3 执行部署操作
-
Docker Compose 方式:
-
关闭旧服务
# amd64系统执行: docker compose --env-file .env --env-file .env.image.amd64 down # arm64系统执行: docker compose --env-file .env --env-file .env.image.arm64 down -
拉取新镜像并重新启动服务
# amd64系统执行: docker compose --env-file .env --env-file .env.image.amd64 up -d # arm64系统执行: docker compose --env-file .env --env-file .env.image.arm64 up -d -
重启nginx,以便后续前端代码部署
make -f Makefile.deploy stop-nginx make -f Makefile.deploy run-nginx
-
十、 第六阶段:线上验证与监控
10.1 验证部署结果
-
再次执行第四阶段和第五阶段修改的功能测试,确保线上环境生效。
10.2 监控与观察
-
查看应用日志,确保无异常错误。
-
观察监控仪表盘(如CPU、内存、请求量、错误率)。
十一、 常见问题排查(Q&A)
-
Q1:
docker build失败,提示某些依赖下载不了? -
A1: 切换至科学上网环境,镜像过大可能会下载时间过久
-
Q2:
docker: no such file or directory错误? -
A2: 镜像平台与运行平台不匹配,非常常见!
-
Q3: 本地测试成功,但部署后无法访问?
-
A3: 检查网络、防火墙、端口映射
-
Q4: 如何回滚到上一个版本?
-
A4: 将.env配置中的镜像Tag改回旧版本,重新部署
-
更多问题详见README中的Q&A
十二、 总结与最佳实践
12.1 流程总结
我们用一张流程图再次梳理 代码 -> 构建 -> 测试 -> 推送 -> 部署 -> 再测试 的完整闭环。

12.2 最佳实践建议
-
版本化: 每次构建都使用唯一的Tag(如Git Commit ID)。
-
安全: 使用
.dockerignore文件,避免将敏感信息打入镜像。 -
不可变基础设施: 始终坚持部署通过验证的镜像Tag,而非在运行中的容器内直接修改代码或配置。任何更改都应通过重新构建和部署新镜像来完成。
-
文档更新: 若二次开发引入了新的配置项或依赖,建议同步更新项目文档
更多推荐


所有评论(0)