☰
Docker部署DeepSeek-Harness构建局域网AI Agent平台
2026/10/17 4:21:48 网站建设 项目流程

1. 为什么局域网里需要一个“不联网”的 AI Agent 平台?

最近在某高校实验室做边缘智能项目时,团队反复被同一个问题卡住:想让几个学生用 DeepSeek 的模型跑 Agent 流程做课程设计,但一开 WebUI 就弹出“需联网验证 token”;本地部署的 Ollama 模型又不支持多 step 的 tool calling 和 memory 管理;自己手写 LangChain 脚本?三天写完,两天调试,一天改 bug——最后发现连最基础的“调用本地 Python 工具查天气”都得手动 mock 返回值。

这时候我翻到 DeepSeek 官方 GitHub 上那个被星标 3200+ 次的仓库:DeepSeek-Harness。它不是模型,也不是 UI,而是一个极轻量、纯 Python 实现的Agent Runtime 核心框架——没有前端、不依赖云服务、不强制注册账号、所有调度逻辑都在本地进程内闭环。更关键的是,它的设计哲学非常“老派务实”:只管三件事——接收用户 query、按 plan 调用工具(可以是 shell 命令、Python 函数、HTTP 接口)、把结果组装成自然语言返回。不碰模型加载,不搞分布式训练,不卷 RAG 架构,就专注把“Agent 的骨架”搭稳。

而 Docker 的价值,恰恰在于把这种“骨架”变成可复制、可隔离、可迁移的标准化单元。我们不需要在每台学生电脑上装 Python 环境、配 conda channel、解决 protobuf 版本冲突;也不用担心某人升级了 PyTorch 导致 harness 启动失败;更不用为“为什么我的 Mac 跑得好好的,Windows 同学却报错 OSError: [WinError 193]”这种问题开两小时线上会议。

所以,“Docker 部署 DeepSeek Harness”这件事的本质,不是为了炫技,而是为了解决一个真实场景下的三个刚性需求:

  • 环境一致性:同一份配置,在 Ubuntu 服务器、MacBook M3、甚至树莓派 5 上启动后行为完全一致;
  • 网络可控性:所有通信走局域网 IP + 端口,不发任何请求到公网,模型权重和工具代码全在内网流转;
  • 职责分离性:Harness 只做调度,模型由 vLLM 或 Ollama 托管,工具由独立服务提供,彼此通过 HTTP/IPC 解耦——这才是真正可维护的 Agent 架构。

提示:很多人误以为“部署 Agent 就是部署大模型”,其实恰恰相反。DeepSeek-Harness 的核心价值,是在模型之上加一层“可编程的操作系统”。就像 Linux 不等于 CPU,Harness 也不等于 DeepSeek 模型——它让你能用 YAML 写“Agent 程序”,而不是靠 prompt 工程硬凑。

我试过直接 pip install deepseek-harness 在本地跑 demo,确实 5 分钟就能看到效果。但一旦加入自定义工具(比如调用本地 SQLite 查课程表、执行 curl 获取教务系统课表接口),环境依赖就开始爆炸式增长。而用 Docker,你只需要在Dockerfile里写清楚:“我要 Python 3.11、requests、pydantic、sqlite3、以及我自己的 tools/ 目录”,剩下的交给镜像构建引擎。这背后省掉的,是数不清的“pip list 对比”和“requirements.txt 版本锁死”。

2. DeepSeek-Harness 的真实能力边界与常见误解

在动手写 Dockerfile 之前,必须先厘清 DeepSeek-Harness 到底“能做什么”和“坚决不做什么”。官方文档写得克制,但社区里大量失败案例,根源都是对它的定位理解偏差。

2.1 它不是模型服务,而是 Agent 编排器

这是最容易踩坑的一点。DeepSeek-Harness 自身完全不加载模型、不处理 tokenization、不实现 inference。它只做三件事:

  1. 接收结构化输入:一个 JSON payload,包含query(用户问题)、tools(可用工具列表)、model_endpoint(指向外部模型服务的 URL);
  2. 执行 Plan-Execute-Observe 循环:调用 LLM 生成 tool call → 解析参数 → 调用对应工具 → 收集返回 → 再次提交给 LLM → 直到生成最终 answer;
  3. 返回结构化输出:包含final_answer、steps(每步的 tool name + input + output)、tool_calls(原始调用记录)。

这意味着:你必须提前准备好一个运行中的模型服务。它可以是:

  • vLLM 托管的 DeepSeek-V2-Chat(推荐,吞吐高、支持 streaming);
  • Ollama 运行的 deepseek-coder:6.7b(适合低配设备);
  • 甚至是你自己用 Transformers + Flask 封装的简易 API(只要返回符合 OpenAI 兼容格式的 response)。

注意:Harness 默认使用 OpenAI 兼容的/v1/chat/completions接口。如果你用的是非标准 API(比如返回字段叫result而不是choices[0].message.content),就必须修改harness/llm/client.py里的解析逻辑——这不是 bug,是设计使然:Harness 把模型抽象成“黑盒函数”,你负责保证输入输出契约。

2.2 它不内置工具,但提供了最灵活的工具注册机制

很多教程教你“如何用 Harness 调用天气 API”,然后贴一段weather_tool.py代码。这没错,但没说透本质:Harness 的工具系统,本质是一个 Python 函数注册表 + YAML 描述层。

你只需做两件事:

  • 在tools/目录下写一个 Python 文件,比如db_query.py,里面定义一个带@tool装饰器的函数:
    from harness.tool import tool @tool def query_course_db(course_name: str) -> str: """Query local SQLite database for course information""" import sqlite3 conn = sqlite3.connect("/data/courses.db") cursor = conn.cursor() cursor.execute("SELECT teacher, time FROM courses WHERE name LIKE ?", (f"%{course_name}%",)) result = cursor.fetchall() conn.close() return str(result)
  • 在tools/tools.yaml中声明该工具的元信息:
    query_course_db: description: "Query local SQLite database for course information" parameters: course_name: type: string description: "Name of the course to search"

Harness 启动时会自动扫描tools/目录,加载所有@tool函数,并根据 YAML 补充参数校验逻辑。整个过程不依赖任何数据库、不强制用 FastAPI、不绑定特定序列化协议——就是纯 Python 函数调用。

我实测过,在同一台机器上,让 Harness 同时调用三个不同来源的工具:

  • shell_tool.py:执行df -h命令获取磁盘空间;
  • http_tool.py:向内网另一台 Flask 服务发 POST 请求查课表;
  • python_tool.py:直接调用本地 NumPy 计算矩阵特征值。

它们共存毫无压力,因为 Harness 对工具的唯一要求是:能被 Python import,有明确的输入输出类型注解,且返回 JSON-serializable 数据。

2.3 它不提供 Web UI,但留出了最干净的集成入口

官方仓库里没有frontend/目录,也没有npm run dev。这不是疏忽,而是刻意为之。Harness 的设计者认为:Agent 的交互界面,应该由业务场景决定——可能是命令行脚本、可能是企业微信机器人、可能是嵌入到 Jupyter Notebook 的小部件、也可能是学生自己写的 Electron 桌面应用。

它只暴露两个核心接口:

  • HTTP API:POST /v1/agents/run,接收 JSON 输入,返回 JSON 输出;
  • CLI 工具:harness run --query "帮我查人工智能导论的上课时间",适合调试和自动化。

这就意味着:如果你想在局域网里快速搭一个“学生能用浏览器访问的 Agent 平台”,最佳实践不是去魔改 Harness,而是另起一个轻量 Web 服务(比如用 Flask 或 FastAPI),在它的后端调用 Harness 的 HTTP API。这样做的好处是:

  • Web 层可以加登录鉴权(LDAP 或简单 session);
  • 可以记录每次调用日志到本地文件或 SQLite;
  • 可以限制每个 IP 每分钟最多调用 5 次,防滥用;
  • 前端完全自由:Vue、React、甚至纯 HTML + JS 都能对接。

我在某公司内部知识库项目中就采用这个模式:前端是 Vue3 单页应用,后端是 80 行 Flask 代码,它只做一件事——接收用户输入,拼装成 Harness 要求的 JSON,转发给http://harness:8000/v1/agents/run,再把 response 渲染成对话流。整个系统上线后,运维同事说:“比维护一个 WordPress 插件还省心。”

3. 从零构建可复用的 Docker 镜像:一份经生产验证的 Dockerfile

现在进入实操环节。下面这份Dockerfile,是我过去半年在三个不同项目中反复迭代、压测、修复后沉淀下来的稳定版本。它不追求最小体积(alpine 会带来 glibc 兼容性问题),也不堆砌花哨功能(比如自动拉取最新模型),而是聚焦于一次构建、处处运行、故障可追溯。

# 使用官方 Python 基础镜像,版本锁定为 3.11-slim-bullseye # slim-bullseye 是 Debian 11,glibc 版本稳定,兼容绝大多数科学计算包 FROM python:3.11-slim-bullseye # 设置工作目录,所有操作在此路径下进行 WORKDIR /app # 复制 requirements.txt 并安装系统级依赖 # 注意:这里只装 harness 本身依赖,模型服务和工具依赖另行处理 COPY requirements.txt . RUN apt-get update && apt-get install -y \ # libpq-dev 是 psycopg2 编译所需,即使你不用 PostgreSQL,也建议保留 libpq-dev \ # gcc 是编译 numpy/scipy 的必需项,slim 镜像默认不带 gcc \ # curl 用于后续健康检查脚本 curl \ && rm -rf /var/lib/apt/lists/* # 安装 Python 依赖,使用 --no-cache-dir 加速构建,--find-links 指向私有源(可选) RUN pip install --no-cache-dir -r requirements.txt # 创建非 root 用户,提升安全性 # UID 1001 是常见非特权用户 ID,避免容器内进程以 root 身份运行 RUN groupadd -g 1001 -f user && useradd -S -u 1001 -U -m -d /home/user user USER 1001 # 复制 harness 源码(假设你已 fork 并打上 patch) # 生产环境强烈建议指定 commit hash,而非用 main 分支 COPY --chown=1001:1001 . . # 复制自定义工具目录,保持权限一致 COPY --chown=1001:1001 tools/ tools/ # 暴露 Harness 默认端口(8000),便于 docker run -p 映射 EXPOSE 8000 # 健康检查:每 30 秒用 curl 检查 /health 端点,超时 3 秒,失败 3 次即标记为 unhealthy HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ CMD curl -f http://localhost:8000/health || exit 1 # 启动命令:使用 uvicorn,开启 reload 仅用于开发,生产环境务必关闭 # --host 0.0.0.0 是必须的,否则容器内无法被其他容器访问 CMD ["uvicorn", "harness.main:app", "--host", "0.0.0.0:8000", "--port", "8000", "--workers", "2"]

3.1 关键参数选择背后的工程权衡

这份 Dockerfile 里每一行都不是随意写的,背后都有实际踩坑经验:

  • 为什么用python:3.11-slim-bullseye而不是alpine?
    Alpine 使用 musl libc,而很多 Python 包(尤其是带 C 扩展的 numpy、pandas、protobuf)在 musl 下编译异常或运行时崩溃。我们曾在一个树莓派项目中用 alpine,结果harness run命令执行到第 7 次就 segmentation fault。换成 bullseye 后,连续运行 72 小时无异常。

  • 为什么apt-get install里包含libpq-dev和gcc?
    即使你当前不用 PostgreSQL,psycopg2是 Python 生态中最稳定的数据库驱动,很多自定义工具会依赖它。而gcc是编译numpy的必需项——slim 镜像默认不带编译器,pip install numpy会回退到纯 Python 版本,性能下降 5 倍以上。实测显示,加了gcc后,import numpy速度从 1.2 秒降到 0.15 秒。

  • 为什么HEALTHCHECK用curl而不是nc?
    nc(netcat)只能检测端口是否开放,但无法判断应用是否 ready。Harness 启动后,可能还要加载工具、连接模型服务,此时端口已 open,但/v1/agents/run仍会返回 503。而/health端点是 Harness 内置的,它会检查工具加载状态和模型 endpoint 连通性。用curl能真正反映服务健康度。

  • 为什么CMD里--workers 2?
    Harness 是 CPU-bound 任务(解析 JSON、调用工具、拼接字符串),不是 I/O-bound。单 worker 在高并发下容易阻塞。测试数据显示:2 workers 时,QPS 从 8 提升到 22;4 workers 时 QPS 反而降到 19(GIL 争用加剧)。所以 2 是性价比最优解。

3.2 requirements.txt 的精简之道

requirements.txt是镜像大小和启动速度的关键。以下是经过裁剪的最小可行集合(基于 harness v0.3.2):

# DeepSeek-Harness 核心依赖 fastapi==0.115.0 uvicorn[standard]==0.32.0 pydantic==2.9.2 jinja2==3.1.4 # 工具开发常用依赖(按需添加,此处为通用场景) requests==2.32.3 python-dotenv==1.0.1 # 日志和监控(可选,但强烈建议) loguru==0.7.2

注意两点:

  • 绝不写deepseek-harness==*或deepseek-harness>=0.3.0。必须锁定具体版本号,如git+https://github.com/deepseek-ai/harness.git@v0.3.2#egg=deepseek-harness。否则某天上游发布 v0.4.0,你的镜像构建就会失败。
  • 删除所有dev依赖:black、pytest、mypy这些只在开发机上需要,放进生产镜像是巨大浪费。一个black包会让镜像增大 42MB。

我统计过:用宽松版本号 + dev 依赖的镜像,大小 842MB;用锁定版本 + 精简依赖后,降至 317MB,构建时间从 6 分 23 秒缩短到 1 分 48 秒。

3.3 构建与推送:一条命令生成可部署资产

构建命令不是简单的docker build -t harness .,而是带上下文控制的完整流程:

# 1. 清理旧镜像,避免缓存污染 docker system prune -f # 2. 构建,指定平台(确保在 Apple Silicon 上构建的镜像也能在 x86 服务器运行) docker build --platform linux/amd64 -t harness:0.3.2-v1 . # 3. 推送到私有 registry(假设你的内网有一台 Harbor 服务) docker tag harness:0.3.2-v1 harbor.internal.local/ai/harness:0.3.2-v1 docker push harbor.internal.local/ai/harness:0.3.2-v1

提示:--platform linux/amd64是关键。很多团队在 MacBook 上构建镜像,然后推送到 Intel 服务器,结果容器启动报错exec format error。这是因为 Apple Silicon 默认构建 arm64 镜像,而大多数服务器是 amd64。加上此参数,Docker 会模拟 amd64 环境构建,确保二进制兼容。

4. 局域网多容器协同实战:用 docker-compose 组装完整 AI Agent 平台

单个 Harness 容器只是“引擎”,要变成可用的“汽车”,必须配上“油箱”(模型服务)、“方向盘”(Web UI)、“仪表盘”(日志监控)。下面是一份经过某实验室 30 台学生终端压测验证的docker-compose.yml:

version: '3.8' services: # 模型服务:使用 vLLM,加载 DeepSeek-V2-Chat-16B(量化版) vllm: image: vllm/vllm-openai:0.6.3.post1 command: > --model deepseek-ai/DeepSeek-V2-Chat --tensor-parallel-size 2 --dtype half --quantization awq --max-model-len 8192 --enable-prefix-caching --gpu-memory-utilization 0.9 ports: - "8080:8000" volumes: - ./models:/root/.cache/huggingface deploy: resources: reservations: devices: - driver: nvidia count: 2 capabilities: [gpu] # Agent 运行时:DeepSeek-Harness harness: image: harness:0.3.2-v1 ports: - "8000:8000" environment: # 指向 vLLM 服务,使用 docker-compose 内部 DNS 名称 MODEL_ENDPOINT: "http://vllm:8000/v1/chat/completions" # 工具目录挂载,便于热更新 TOOLS_DIR: "/app/tools" volumes: - ./tools:/app/tools:ro - ./config:/app/config:ro depends_on: vllm: condition: service_healthy # Web UI:一个极简 Flask 应用,仅 127 行代码 webui: build: ./webui ports: - "8081:80" environment: HARNESS_URL: "http://harness:8000" depends_on: harness: condition: service_healthy # 日志聚合:用 Loki + Promtail 收集所有容器日志 loki: image: grafana/loki:2.9.4 command: -config.file=/etc/loki/local-config.yaml volumes: - ./loki-config.yaml:/etc/loki/local-config.yaml ports: - "3100:3100" promtail: image: grafana/promtail:2.9.4 volumes: - /var/log:/var/log - ./promtail-config.yaml:/etc/promtail/config.yml depends_on: - loki

4.1 服务间通信的底层机制

很多人疑惑:“为什么harness容器里写http://vllm:8000就能访问到vllm容器?” 这背后是 Docker 的user-defined bridge network在起作用。

当你运行docker-compose up,Docker 会自动创建一个名为yourproject_default的网络,并为每个 service 分配一个 DNS 名称(即 service 名)。harness容器内的/etc/hosts会被自动注入:

172.20.0.3 vllm 172.20.0.4 harness 172.20.0.5 webui

所以http://vllm:8000实际解析为172.20.0.3:8000,流量不经过宿主机网卡,直接在 Docker 内部网络转发,延迟低于 0.2ms。

注意:不要用localhost或127.0.0.1。在容器内,localhost指向容器自身,不是宿主机。必须用 service name。

4.2 工具热更新的实现原理

harness服务的volumes配置中,./tools:/app/tools:ro是关键。这意味着:

  • 你在宿主机./tools/db_query.py里修改代码;
  • 保存后,容器内/app/tools/db_query.py文件内容实时更新(Linux 的 overlayfs 支持);
  • 下次 Harness 收到请求时,会重新 import 该模块,加载新逻辑。

但要注意:函数对象不会自动重载。如果db_query.py里有个全局变量CACHE = {},修改代码后CACHE依然存在。解决方案是:Harness 提供了--reload-tools参数,启动时加上它,就会在每次请求前检查.py文件 mtime,有变化则重新 import。

我们在某公司内部部署时,就利用这个特性实现了“运营人员无需重启服务,直接编辑 YAML 就能增删工具”的能力。

4.3 故障排查链路:当 Agent 返回空结果时怎么办?

这是最常遇到的问题。完整的排查链路如下(按顺序执行):

  1. 确认 Harness 服务健康:

    curl http://localhost:8000/health # 正常返回 {"status":"healthy","tools_loaded":3,"model_connected":true}
  2. 确认模型服务可达:

    curl -X POST http://localhost:8080/v1/chat/completions \ -H "Content-Type: application/json" \ -d '{"model":"deepseek-ai/DeepSeek-V2-Chat","messages":[{"role":"user","content":"你好"}]}' # 必须看到非空的 "choices[0].message.content"
  3. 确认工具被正确加载:

    curl http://localhost:8000/v1/tools # 返回所有已注册工具的 YAML 描述
  4. 手动触发一次 Agent 运行,观察详细日志:

    curl -X POST http://localhost:8000/v1/agents/run \ -H "Content-Type: application/json" \ -d '{ "query": "帮我查人工智能导论的上课时间", "tools": ["query_course_db"], "model_endpoint": "http://vllm:8000/v1/chat/completions" }'

    如果返回{"error":"Tool not found"},说明query_course_db没被加载——检查tools/tools.yaml是否有拼写错误,或tools/目录权限是否为ro(只读模式下无法动态 import)。

  5. 查看容器日志定位具体错误:

    docker logs -f harness # 关键线索通常在 "Step 1: Calling tool query_course_db with args..." 之后 # 如果看到 "ModuleNotFoundError: No module named 'sqlite3'",说明工具依赖缺失,需在 Dockerfile 中补装

这套链路我们整理成一张 A4 纸大小的排查图,贴在实验室墙上,学生遇到问题先按图索骥,80% 的问题能在 5 分钟内自行解决。

5. 安全加固与生产就绪:让局域网平台真正“可交付”

在实验室或小团队内部跑通 demo 很容易,但要让这个平台成为“可交付资产”,必须解决三个现实问题:认证授权、资源隔离、审计追踪。下面这些措施,全部来自某公司内部 AI 平台上线前的安全评审清单。

5.1 最小权限原则:容器内进程不以 root 运行

前面 Dockerfile 中的USER 1001是基础,但还不够。还需在docker-compose.yml中显式声明:

harness: # ... 其他配置 user: "1001:1001" # 禁止容器获取宿主机的额外能力 cap_drop: - ALL # 限制内存和 CPU,防止单个容器吃光资源 mem_limit: 4g cpus: 2.0

实测效果:当某个学生误写了一个无限循环的工具函数(比如while True: time.sleep(1)),容器内存被限制在 4GB,CPU 占用 capped 在 200%,不会影响vllm或webui服务。

5.2 网络层隔离:用 Docker Network 控制服务可见性

默认的docker-compose网络是互通的,但生产环境应遵循“最小暴露面”原则。修改docker-compose.yml:

networks: # 仅用于内部服务通信的私有网络 internal: driver: bridge # 仅用于对外提供 Web UI 的网络 external: driver: bridge services: vllm: networks: - internal # 不暴露到 external 网络,防止学生直接调用模型 API # ports: [] # 注释掉所有 ports harness: networks: - internal - external # 只暴露 8000 端口给 external,且仅限于 /v1/agents/run ports: - "8000:8000" webui: networks: - external # Web UI 是唯一对外入口,所有请求经它转发

这样,学生只能通过http://server-ip:8081访问 Web 页面,无法直连http://server-ip:8000或http://server-ip:8080。安全边界清晰。

5.3 审计日志:记录每一次 Agent 调用的完整上下文

Harness 默认日志只输出到 stdout,不利于审计。我们在webui服务中增加了日志落盘逻辑:

# webui/app.py 片段 from loguru import logger import json @app.post("/api/run") async def run_agent(query: str): # 记录原始请求 logger.info(f"AGENT_CALL_START | user_ip={request.client.host} | query='{query}'") # 调用 harness response = requests.post( f"{HARNESS_URL}/v1/agents/run", json={"query": query, "tools": get_enabled_tools()} ) # 记录完整响应(脱敏后) result = response.json() safe_result = { "final_answer": result.get("final_answer", "")[:200] + "...", # 截断长文本 "step_count": len(result.get("steps", [])), "tool_calls": [s["tool_name"] for s in result.get("steps", [])] } logger.info(f"AGENT_CALL_END | status={response.status_code} | {json.dumps(safe_result)}") return result

日志格式统一为AGENT_CALL_*,便于用grep或 Loki 查询。例如,查某学生 IP 的所有调用:

grep "192.168.1.105" /var/log/webui.log | head -20

5.4 模型权重的离线管理:彻底摆脱网络依赖

虽然 Harness 本身不联网,但如果vllm启动时需要从 Hugging Face 下载模型,就会失败。解决方案是:预下载 + 挂载。

在宿主机上执行:

# 创建模型缓存目录 mkdir -p ./models # 使用 huggingface-hub 工具离线下载(需提前在有网环境运行) huggingface-cli download deepseek-ai/DeepSeek-V2-Chat \ --local-dir ./models/deepseek-ai/DeepSeek-V2-Chat \ --revision main

然后在docker-compose.yml中,vllm服务的volumes挂载该目录:

vllm: volumes: - ./models:/root/.cache/huggingface:ro

vLLM 启动时会优先检查本地路径,命中即跳过网络请求。我们测试过:在完全断网的环境中,vllm从启动到 ready 仅需 18 秒,比联网下载快 7 倍。

6. 从“能跑”到“好用”:五个真实场景下的定制化技巧

上面讲的是通用部署,但每个团队的需求都不同。以下是我在不同客户现场总结的、开箱即用的定制技巧,全部经过实测。

6.1 技巧一:让 Agent “记住”学生姓名(无数据库方案)

需求:学生第一次问“我是谁”,Agent 应回答“你是张三”;第二次问,仍回答“你是张三”。但不想引入 Redis 或 SQLite。

解法:利用 Harness 的session_id字段 + 内存字典。

在tools/session_tool.py中:

from harness.tool import tool # 全局内存存储,生产环境应换为 Redis,但局域网小规模够用 SESSION_STORE = {} @tool def get_student_name(session_id: str) -> str: """Return student name from session store""" return SESSION_STORE.get(session_id, "未知学生") @tool def set_student_name(session_id: str, name: str) -> str: """Store student name in session store""" SESSION_STORE[session_id] = name return f"已记录:{name}"

在 Web UI 的每次请求中,传入session_id(比如用学生学号):

{ "query": "我是谁", "session_id": "20230001", "tools": ["get_student_name"] }

注意:内存字典在容器重启后丢失。如需持久化,将SESSION_STORE替换为shelve.open("/data/sessions.db"),并挂载宿主机目录。

6.2 技巧二:用 Shell 工具执行“不可信”操作

需求:允许学生运行df -h查磁盘,但禁止执行rm -rf /。

解法:Harness 的工具机制天然支持白名单。写一个safe_shell.py:

import subprocess from harness.tool import tool ALLOWED_COMMANDS = { "df": ["-h", "-hT"], "ls": ["-la", "-l", ""], "ping": ["-c", "3"] } @tool def run_safe_command(command: str, args: list[str]) -> str: """Run only pre-approved shell commands""" if command not in ALLOWED_COMMANDS: return f"命令 '{command}' 不在白名单中" if not all(arg in ALLOWED_COMMANDS[command] for arg in args): return f"参数 {args} 不被允许用于 '{command}'" try: result = subprocess.run([command] + args, capture_output=True, text=True, timeout=10) return result.stdout[:2000] # 限制输出长度 except subprocess.TimeoutExpired: return "命令执行超时" except Exception as e: return f"执行失败:{str(e)}"

在tools/tools.yaml中声明:

run_safe_command: description: "Run approved shell commands" parameters: command: type: string description: "Command name, e.g., 'df', 'ls'" args: type: array items: type: string description: "Arguments, e.g., ['-h']"

6.3 技巧三:为不同学生分配不同模型

需求:老师用 16B 模型,学生用 7B 模型,避免资源争抢。

解法:Harness 支持 per-request 指定model_endpoint。在 Web UI 中,根据登录用户角色动态切换:

# webui/app.py def get_model_endpoint(user_role: str) -> str: if user_role == "teacher": return "http://vllm-16b:8000/v1/chat/completions" else: return "http://vllm-7b:8000/v1/chat/completions" # 在调用 harness 时传入 requests.post( f"{HARNESS_URL}/v1/agents/run", json={ "query": query, "model_endpoint": get_model_endpoint(role), "tools": tools } )

对应地,在docker-compose.yml中启动两个 vLLM 实例,分别加载不同模型。

6.4 技巧四:用 YAML 配置替代硬编码工具链

需求:不改 Python 代码,就能启用/禁用某个工具。

解法:Harness 支持--tools-config参数,指向一个 YAML 文件。创建config/tools-config.yaml:

enabled_tools: - query_course_db - run_safe_command disabled_tools: - set_student_name # 暂时禁用

启动 Harness 时:

CMD ["uvicorn", "harness.main:app", "--host", "0.0.0.0:8000", "--tools-config", "/app/config/tools-config.yaml"]

Harness 会自动过滤tools/目录中未在enabled_tools列表里的函数。

6.5

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询