快速搭建 JDGenie:Docker 部署与 DeepSeek + 博查搜索配置指南
京东开源的多智能体平台JDGenie提供了开箱即用的端到端解决方案。本文介绍了如何通过Docker快速部署JDGenie,并配置DeepSeek模型和博查搜索功能。内容包括:1)克隆仓库;2)更新模型配置文件;3)构建Docker镜像(提供了镜像源优化和构建失败解决方案);4)配置DeepSeek API和博查搜索API密钥。该部署方案使JDGenie具备长文本理解和实时联网检索能力,适合开发者快
快速搭建 JDGenie:Docker 部署与 DeepSeek + 博查搜索配置指南
一、JoyAgent-JDGenie
在多智能体(Multi-Agent)的开源探索里,大多数项目只提供 SDK 或框架,开发者需要自己去拼接模型、搜索、任务调度,才能做出一个能用的产品。这样不仅门槛高,还很难快速落地。
JoyAgent-JDGenie 是京东开源的首个开箱即用的端到端多智能体产品,包含前后端、执行引擎和一系列内置智能体(报告生成、代码助手、文件处理等)。简单来说,JDGenie 不是一个“工具箱”,而是一台能直接跑起来的“多智能体机器”。
更重要的是,JDGenie 保持了高度的可拓展性。开发者只需挂载新的子智能体或工具,就能轻松为它增加新能力。例如:
- 接入 DeepSeek 模型,带来更强的长上下文理解与生成能力;
- 配置 博查 Web Search,让智能体能够实时获取外部信息,而不是局限在静态知识。
通过这种方式,JDGenie 从一个“通用多智能体框架”,升级为一个既能回答问题,又能联网检索、生成多种交付形式(网页、Markdown、PPT)的实用智能体平台。
也正因如此,JDGenie 特别适合开发者快速上手,并在此基础上做二次开发。
本篇文章将介绍如何将 DeepSeek 与博查搜索无缝接入,打造一个更智能、更实时的多Agent系统。
二、快速开始 —— docker启动JDGenie
1)克隆仓库
git clone https://github.com/jd-opensource/joyagent-jdgenie.git
2)更新模型配置
1. 更新 application.yml
找到genie后端文件夹下的 genie-backend/src/main/resources/application.yml
。
手动更新其中的base_url
、apikey
、model
、max_tokens
、model_name
等配置。
使用DeepSeek时,注意 deepseek-chat
的 max_tokens
为 8192
其中,DEEPSEEK_API_KEY可以选择使用环境变量或直接写入项目配置文件,推荐写入环境变量中,直接在启动docker时注入。
2. 更新 .env_template
手动更新 genie-tool/.env_template
中的 OPENAI_API_KEY
、OPENAI_BASE_URL
、DEFAULT_MODEL
、SERPER_SEARCH_API_KEY
使用DeepSeek时: 设置DEEPSEEK_API_KEY
、DEEPSEEK_API_BASE
,DEFAULT_MODEL
设置为 deepseek/deepseek-chat
,所有 ${DEFAULT_MODEL}
也都改成 deepseek/deepseek-chat
3)编译dockerfile
启动Docker Desktop,在Docker Engine里添加镜像源,并点击应用。添加镜像源可以增加构建成功率。
"registry-mirrors": [
"https://2a6bf1988cb6428c877f723ec7530dbc.mirror.swr.myhuaweicloud.com",
"https://docker.m.daocloud.io",
"https://hub-mirror.c.163.com",
"https://mirror.baidubce.com",
"https://your_preferred_mirror",
"https://dockerhub.icu",
"https://docker.registry.cyou",
"https://docker-cf.registry.cyou",
"https://dockercf.jsdelivr.fyi",
"https://docker.jsdelivr.fyi",
"https://dockertest.jsdelivr.fyi",
"https://mirror.aliyuncs.com",
"https://dockerproxy.com",
"https://mirror.baidubce.com",
"https://docker.m.daocloud.io",
"https://docker.nju.edu.cn",
"https://docker.mirrors.sjtug.sjtu.edu.cn",
"https://docker.mirrors.ustc.edu.cn",
"https://mirror.iscas.ac.cn",
"https://docker.rainbond.cc"
]
在控制台输入下面的命令开始构建:
docker build -t genie:latest .
问题1:构建失败
可以参考github-issue分享的修改后的Dockerfile,将其替换拉取下来的本地Dockerfile。原址:https://github.com/jd-opensource/joyagent-jdgenie/issues/247
# 前端构建阶段
FROM docker.m.daocloud.io/library/node:20-alpine AS frontend-builder
WORKDIR /app
RUN npm install -g pnpm
COPY ui/package.json ./
RUN npm config set registry https://registry.npmmirror.com
RUN pnpm install
COPY ui/ .
RUN pnpm build
# 后端构建阶段
FROM docker.m.daocloud.io/library/maven:3.8-openjdk-17 AS backend-builder
WORKDIR /app
COPY genie-backend/pom.xml .
COPY genie-backend/src ./src
COPY genie-backend/build.sh genie-backend/start.sh ./
RUN chmod +x build.sh start.sh
RUN ./build.sh
# Python 环境准备阶段
FROM docker.m.daocloud.io/library/python:3.11 AS python-base
WORKDIR /app
RUN rm /etc/apt/sources.list.d/* && echo 'deb https://mirrors.aliyun.com/debian/ bookworm main contrib non-free non-free-firmware' \
> /etc/apt/sources.list && \
echo 'deb https://mirrors.aliyun.com/debian-security bookworm-security main contrib non-free non-free-firmware' \
>> /etc/apt/sources.list && \
echo 'deb https://mirrors.aliyun.com/debian/ bookworm-updates main contrib non-free non-free-firmware' \
>> /etc/apt/sources.list
RUN apt-get clean && \
apt-get update && \
apt-get install -y --no-install-recommends netcat-openbsd procps curl && \
apt-get install -y --no-install-recommends --allow-change-held-packages build-essential && \
rm -rf /var/lib/apt/lists/*
RUN pip install uv
# 最终运行阶段
FROM docker.m.daocloud.io/library/python:3.11
# 安装系统依赖
RUN rm /etc/apt/sources.list.d/* && echo 'deb https://mirrors.aliyun.com/debian/ bookworm main contrib non-free non-free-firmware' \
> /etc/apt/sources.list && \
echo 'deb https://mirrors.aliyun.com/debian-security bookworm-security main contrib non-free non-free-firmware' \
>> /etc/apt/sources.list && \
echo 'deb https://mirrors.aliyun.com/debian/ bookworm-updates main contrib non-free non-free-firmware' \
>> /etc/apt/sources.list
RUN apt-get clean && \
apt-get update && \
apt-get install -y --no-install-recommends \
openjdk-17-jre-headless \
netcat-openbsd \
procps \
curl \
&& rm -rf /var/lib/apt/lists/*
RUN apt-get update && \
apt-get install -y --no-install-recommends \
nodejs \
npm \
&& rm -rf /var/lib/apt/lists/* \
&& npm install -g pnpm
# 设置工作目录
WORKDIR /app
# 复制前端构建产物
COPY --from=frontend-builder /app/dist /app/ui/dist
COPY --from=frontend-builder /app/package.json /app/ui/package.json
COPY --from=frontend-builder /app/node_modules /app/ui/node_modules
# 复制后端构建产物
COPY --from=backend-builder /app/target /app/backend/target
COPY genie-backend/start.sh /app/backend/
RUN chmod +x /app/backend/start.sh
# 复制 Python 工具和依赖
COPY --from=python-base /usr/local/lib/python3.11 /usr/local/lib/python3.11
COPY --from=python-base /usr/local/bin/uv /usr/local/bin/uv
# 复制 genie-client
WORKDIR /app/client
COPY genie-client/pyproject.toml genie-client/uv.lock ./
COPY genie-client/app ./app
COPY genie-client/main.py genie-client/server.py genie-client/start.sh ./
RUN chmod +x start.sh && \
uv venv .venv && \
. .venv/bin/activate && \
export UV_DEFAULT_INDEX="https://pypi.tuna.tsinghua.edu.cn/simple" && uv sync
# 复制 genie-tool
WORKDIR /app/tool
COPY genie-tool/pyproject.toml genie-tool/uv.lock ./
COPY genie-tool/genie_tool ./genie_tool
COPY genie-tool/server.py genie-tool/start.sh genie-tool/.env_template ./
# 创建虚拟环境并安装依赖
RUN chmod +x start.sh && \
uv venv .venv && \
. .venv/bin/activate && \
export UV_DEFAULT_INDEX="https://pypi.tuna.tsinghua.edu.cn/simple" && uv sync && \
mkdir -p /data/genie-tool && \
cp .env_template .env && \
python -m genie_tool.db.db_engine
# 设置数据卷
VOLUME ["/data/genie-tool"]
# 复制统一启动脚本
WORKDIR /app
COPY start_genie.sh .
RUN chmod +x start_genie.sh
EXPOSE 3000 8080 1601
# 健康检查
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD curl -f http://localhost:3000 || exit 1
# 启动所有服务
CMD ["./start_genie.sh"]
最终输出Building FINISHED
即为构建成功。
问题2:=> ERROR [backend-builder 7/7] RUN ./build.sh
在Windows系统内,可能因为CRLF行尾导致构建失败,此时可以更新 Dockerfile 与 build.sh 以规避 Windows CRLF 行结尾导致的 /bin/bash^M 错误并让构建直接成功。
修改后的Dockerfile
:
# 后端构建阶段
FROM docker.m.daocloud.io/library/maven:3.8-openjdk-17 AS backend-builder
WORKDIR /app
COPY genie-backend/pom.xml .
COPY genie-backend/src ./src
COPY genie-backend/build.sh genie-backend/start.sh ./
# 修改前代码:
# RUN chmod +x build.sh start.sh
# RUN ./build.sh
# 修改后代码:
# 处理可能的 Windows CRLF 行尾并执行构建
RUN sed -i 's/\r$//' build.sh start.sh \
&& chmod +x build.sh start.sh \
&& ./build.sh
修改后的build.sh
:
# build.sh
#!/usr/bin/env bash
set -euo pipefail
# 需安装 jdk17 (容器内基础镜像已提供 openjdk-17)
# 需安装 maven3 (基础镜像 maven:3.8-openjdk-17 已包含)
# 生成临时 settings(使用阿里云镜像加速)
cat > aliyun-settings.xml <<'EOF'
<settings>
<mirrors>
<mirror>
<id>aliyun</id>
<url>https://maven.aliyun.com/repository/public</url>
<mirrorOf>*</mirrorOf>
</mirror>
</mirrors>
</settings>
EOF
echo "[build.sh] Using mirror settings, start maven build..."
mvn -B -DskipTests -s aliyun-settings.xml clean package
echo "[build.sh] Maven build finished."
问题3:exec ./start_genie.sh: no such file or directory
为修复容器启动找不到 start_genie.sh
的问题,需要修改 Dockerfile:安装 bash 并对 start_genie.sh
做行尾转换。
# 复制统一启动脚本
WORKDIR /app
COPY start_genie.sh .
# 修改前代码:
# RUN chmod +x start_genie.sh
# 修改后代码
# 统一行尾并赋权;再递归处理所有脚本,避免 CRLF 导致的隐藏错误
RUN sed -i 's/\r$//' start_genie.sh && chmod +x start_genie.sh \
&& find /app -maxdepth 6 -type f -name '*.sh' -exec sed -i 's/\r$//' {} + \
&& find /app -maxdepth 6 -type f -name '*.sh' -exec chmod +x {} + \
&& bash -c 'echo "[debug] bash version: $(bash --version | head -1)"'
参考csdn:https://blog.csdn.net/m0_62128476/article/details/137530737
问题4: 无法识别.env_template
内的URL等参数
将为确保 .env
中 URL 被正确去除 CRLF 并在新 exec 会话可见,修改 Dockerfile
(清理 .env 行尾)与 start_genie.sh
(把全部 .env 变量写入 /etc/environment)。
在Dockerfile
中添加一行:
sed -i 's/\r$//' .env_template .env && \
修改start_genie.sh
#!/bin/bash
# 添加调试信息
echo "当前工作目录: $(pwd)"
echo "目录列表:"
ls -la
# 开始启动前端服务
echo "尝试进入ui目录..."
if [ -d "ui" ]; then
cd ui
echo "当前目录: $(pwd)"
echo "ui目录内容:"
ls -la
# UI目录没有start.sh,使用npm运行预览服务器
if [ -f "package.json" ]; then
echo "启动UI预览服务器"
echo "修改为监听所有接口(0.0.0.0)并使用端口3000"
# 使用--host和--port参数明确指定监听地址和端口
pnpm preview --host 0.0.0.0 --port 3000 &
# 等待服务启动
sleep 3
# 检查服务是否正在监听(使用更通用的方法,不依赖netstat)
echo "检查服务监听状态:"
if [ -n "$(ps aux | grep 'vite preview' | grep -v grep)" ]; then
echo "Vite预览服务器已启动"
else
echo "警告: Vite预览服务器可能未正确启动"
fi
else
echo "错误: ui/package.json文件不存在"
fi
cd ..
else
echo "错误: ui目录不存在"
fi
# 开始启动后端服务
echo "尝试进入backend目录..."
if [ -d "backend" ]; then
cd backend
echo "当前目录: $(pwd)"
echo "backend目录内容:"
ls -la
if [ -f "start.sh" ]; then
echo "执行backend/start.sh"
sh start.sh
else
echo "错误: backend/start.sh文件不存在,尝试直接启动jar文件"
if [ -f "app.jar" ]; then
echo "启动后端应用"
java -jar app.jar &
else
echo "错误: backend/app.jar文件不存在"
fi
fi
cd ..
else
echo "错误: backend目录不存在"
fi
# 开始启动工具服务
echo "尝试进入tool目录..."
if [ -d "tool" ]; then
cd tool
echo "当前目录: $(pwd)"
echo "tool目录内容:"
ls -la
# 处理环境变量
echo "处理环境变量..."
if [ -f ".env_template" ] && [ ! -f ".env" ]; then
cp .env_template .env
echo "已复制.env_template到.env"
fi
if [ -f ".env" ]; then
echo "更新.env文件并导出环境变量..."
# 如果外部提前通过 -e 传入,则覆盖 .env 中对应行
update_env_var() {
local key="$1" val="$2";
if [ -n "$val" ]; then
if grep -q "^${key}=" .env; then
sed -i "s|^${key}=.*|${key}=${val}|" .env
else
echo "${key}=${val}" >> .env
fi
fi
}
update_env_var OPENAI_API_KEY "$OPENAI_API_KEY"
update_env_var OPENAI_BASE_URL "$OPENAI_BASE_URL"
update_env_var DEEPSEEK_API_KEY "$DEEPSEEK_API_KEY"
update_env_var DEEPSEEK_API_BASE "$DEEPSEEK_API_BASE"
update_env_var BOCHA_WEB_SEARCH_URL "$BOCHA_WEB_SEARCH_URL"
update_env_var BOCHA_API_KEY "$BOCHA_API_KEY"
echo "从.env文件导出环境变量..."
# 读取键=值形式,忽略注释与空行,避免 xargs 在值含空格时截断
while IFS='=' read -r k v; do
if [ -n "$k" ] && [ "${k#'#'}" = "$k" ]; then
export "$k"="${v}"
fi
done < <(grep -v '^#' .env | sed '/^$/d')
# 写入 /etc/environment(避免重复累积同一键:先删除再追加)
persist_env_var() {
local key="$1" val="${!1}";
if [ -n "$val" ]; then
sed -i "/^${key}=*/d" /etc/environment 2>/dev/null || true
echo "${key}=${val}" >> /etc/environment
fi
}
persist_env_var OPENAI_API_KEY
persist_env_var OPENAI_BASE_URL
persist_env_var OPENAI_API_BASE
persist_env_var DEEPSEEK_API_KEY
persist_env_var DEEPSEEK_API_BASE
persist_env_var BOCHA_WEB_SEARCH_URL
persist_env_var BOCHA_API_KEY
echo "环境变量已导出,示例: OPENAI_API_KEY=${OPENAI_API_KEY:0:4}*** BOCHA_WEB_SEARCH_URL=$BOCHA_WEB_SEARCH_URL"
echo "Bocha Key 长度: ${#BOCHA_API_KEY}"
else
echo "警告: .env文件不存在,无法导出环境变量"
fi
# 初始化数据库
echo "初始化数据库..."
if [ -d ".venv" ]; then
. .venv/bin/activate
echo "执行数据库初始化..."
python -m genie_tool.db.db_engine
echo "数据库初始化完成"
else
echo "错误: 虚拟环境不存在,无法初始化数据库"
fi
if [ -f "start.sh" ]; then
echo "执行tool/start.sh"
sh start.sh &
echo "[调试] 运行期可见: BOCHA_WEB_SEARCH_URL=$BOCHA_WEB_SEARCH_URL BOCHA_API_KEY_LEN=${#BOCHA_API_KEY}"
else
echo "错误: tool/start.sh文件不存在"
fi
cd ..
else
echo "错误: tool目录不存在"
fi
# 开始启动MCP服务
echo "尝试进入client目录..."
if [ -d "client" ]; then
cd client
echo "当前目录: $(pwd)"
echo "client目录内容:"
ls -la
if [ -f "start.sh" ]; then
echo "执行client/start.sh"
sh start.sh &
else
echo "错误: client/start.sh文件不存在"
fi
cd ..
else
echo "错误: client目录不存在"
fi
# 保持容器运行
echo "所有服务已启动,保持容器运行..."
tail -f /dev/null
其他问题
如有其他构建问题,可以参考官方issues内的相关讨论。
https://github.com/jd-opensource/joyagent-jdgenie/issues
4)启动dockerfile
若容器名被占用,需要先删除旧容器,再重新启动:
docker ps -a --filter "name=genie-app"
docker rm -f genie-app
docker run -d -p 3000:3000 -p 8080:8080 -p 1601:1601 --name genie-app genie:latest
如果需要注入DEEPSEEK_API_KEY等参数,可以使用下面的启动命令:
docker run -d --name genie-app `
-e DEEPSEEK_API_KEY=$Env:DEEPSEEK_API_KEY `
-p 3000:3000 -p 8080:8080 -p 1601:1601 `
genie:latest
5)访问Genie
在浏览器输入 localhost:3000 访问 Genie
使用示例:
三、在 deepsearch 内配置博查搜索引擎
博查AI Web Search API 是一个强大的全网搜索引擎接口,专为开发者和AI系统设计。在下面这篇博客里,有该API和API KEY获取的详细介绍。
博查AI Web Search API 使用指南:全网搜索网页与图片信息
就在现在,博查AI开放平台全新上线资源包计费方式,最低3.6元/千次!
所有用户均享受以下权益:
- 领取1个免费1000次调用资源包;
- 口令兑换1个免费1000次调用资源包(后续平台还将持续发放新口令,敬请关注,公众号:博查AI);
- 领取10个3.6元/千次调用资源包;
- DeepResearch项目免费领取最高100万次调用次数。
领取链接:博查AI开放平台。
兑换方式:前往开放平台首页,点击“兑换”,输入口令 博查搜索
,点击确认即可。
1)配置博查搜索API
在.env_template
内设置 BOCHA_WEB_SEARCH_URL
,此处我们使用博查官方的Web Search APIBOCHA_API_KEY
同样可以使用docker启动时注入或直接写入配置文件的方式。
搜索引擎原始默认值为serp,我们在搜索引擎列表内添加上bocha
2)更新 search_engine.py
在 search_engine.py
中配置博查搜索类 BochaSearch
.
class BochaSearch(SearchBase):
"""博查搜索引擎实现
环境变量:
BOCHA_WEB_SEARCH_URL e.g. https://api.bochaai.com/v1/web-search
BOCHA_API_KEY 以 Bearer 方式放入 Authorization 头
支持参数:count(复用 SEARCH_COUNT),始终附带 summary=true 方便聚合。
"""
def __init__(self):
super().__init__()
self._engine = "bocha_web_search"
self._url = os.getenv("BOCHA_WEB_SEARCH_URL")
raw_key = os.getenv("BOCHA_API_KEY")
self._api_key = self._sanitize(raw_key)
if raw_key and raw_key != self._api_key:
logger.debug("BochaSearch API Key 含有换行已清理")
self.headers = {
"Content-Type": "application/json",
}
if self._api_key:
self.headers["Authorization"] = f"Bearer {self._api_key}"
def construct_body(self, query: str, request_id: str = None):
return {
"query": query,
"summary": True, # 要求带摘要,利于下游 LLM 使用
"count": min(max(self._count, 1), 10), # 接口限制 1-10
# 可扩展 freshness: "noLimit" | "oneDay" | "oneWeek" | "oneMonth" | "oneYear"
}
async def search(self, query: str, request_id: str = None, *args, **kwargs) -> List[Doc]:
if not self._url or not self._api_key:
logger.warning("BochaSearch 缺少 BOCHA_WEB_SEARCH_URL 或 BOCHA_API_KEY,返回空结果。")
return []
body = self.construct_body(query, request_id)
try:
async with aiohttp.ClientSession() as session:
async with session.post(self._url, json=body, headers=self.headers, timeout=self._timeout) as response:
text = await response.text()
if response.status != 200:
logger.warning(f"BochaSearch 接口非 200: status={response.status} body={text[:300]}")
return []
result = json.loads(text)
except Exception as e:
logger.warning(f"BochaSearch 调用异常: {e}")
return []
data = (result or {}).get("data", {})
web_pages = data.get("webPages", {})
value_list = web_pages.get("value", [])
docs = []
for item in value_list:
title = item.get("name", "")
url = item.get("url", "")
snippet = item.get("snippet") or ""
summary = item.get("summary") or ""
content = summary or snippet # 优先摘要
docs.append(
Doc(
doc_type="web_page",
content=content,
title=title,
link=url,
data={"search_engine": self._engine},
)
)
logger.info(f"BochaSearch 返回 {len(docs)} 条结果 query={query}")
return docs
可以在搜索基类内添加API KEY清理方法,防止换行符导致读取KEY异常。
接着,在 MixSearch
方法中添加博查搜索:
3)更新 deepsearch.py
在deepsearch.py
内搜索引擎初始化处,加入博查搜索引擎
class DeepSearch:
"""深度搜索工具"""
def __init__(self, engines: List[str] = []):
if not engines:
engines = os.getenv("USE_SEARCH_ENGINE", "bing").split(",")
# 清洗引擎名称,避免携带 \r 或多余空格导致匹配失败
engines = [e.replace("\r", "").replace("\n", "").strip().lower() for e in engines if e and e.strip()]
use_bing = "bing" in engines
use_jina = "jina" in engines
use_sogou = "sogou" in engines
use_serp = "serp" in engines
use_bocha = "bocha" in engines or "bocha_web_search" in engines
logger.info(
f"DeepSearch 初始化 引擎配置 engines={engines} -> bing={use_bing} jina={use_jina} sogou={use_sogou} serp={use_serp} bocha={use_bocha}"
)
self._search_single_query = partial(
MixSearch().search_and_dedup,
use_bing=use_bing,
use_jina=use_jina,
use_sogou=use_sogou,
use_serp=use_serp,
use_bocha=use_bocha,
)
self.searched_queries = []
self.current_docs = []
4)重新编译并启动Genie
重新编译dockerfile
docker build -t genie:latest .
若容器名被占用,需要先删除旧容器:
docker ps -a --filter "name=genie-app"
docker rm -f genie-app
携带API KEY等参数重新启动容器:
docker run -d --name genie-app `
-e DEEPSEEK_API_KEY=$Env:DEEPSEEK_API_KEY `
-e BOCHA_API_KEY=$Env:BOCHA_API_KEY `
-e USE_SEARCH_ENGINE=bocha `
-p 3000:3000 -p 8080:8080 -p 1601:1601 `
genie:latest
5)搜索示例:询问今日天气
配置博查搜索引擎前:
仅配置deepseek未配置搜索组件时,无法通过deepsearch得到当前实时天气。
下面对比一下配置 Bocha-Web-Search博查搜索引擎 之后的搜索和回答效果:
Agent成功初始化了博查搜索引擎并进行了调用,查看控制台可以发现,BochaSearch返回了其检索到的相关网页:
搜索完成后,大模型将进一步根据搜索结果生成Web输出文件:
最终输出结果包含精简总结和详细的HTML、Markdown文件。
工作空间内包含了本次提问涉及的网页搜索结果和输出文件:
1. 搜索结果:
点击可查看检索到的网页详情:
2. 输出文件:
输出文件为markdown和html两种,可以在输出结果文件内查看,文件提供了下载和复制两种保存方式,点击文件内的角标还可以快捷跳转到博查搜索到的相关参考网页。
更多推荐
所有评论(0)