资讯详情

DeepSeek Harness实战:从零搭建能调用工具的AI Agent

📅 2026/9/11 7:22:30 | 华诺云谱 👁 阅读
DeepSeek Harness实战:从零搭建能调用工具的AI Agent
第一次在终端里敲下启动命令看着 DeepSeek Harness 把一个看似普通的问答任务拆解成“理解意图—调用工具—汇总结果”三个步骤并自动执行完我还是挺感慨的。过去大半年我一直在折腾各种 Agent 框架从纯手写提示词循环到接 LangGraph总感觉要么太底层、要么太笨重。DeepSeek Harness 给我的第一印象是它把 Agent 开发里那些脏活累活上下文管理、工具调用循环、模型切换都收进去了留给你的核心工作就一件——定义清楚你的 Agent 要做什么、能用哪些工具。这篇文章就围绕我从零到一搭起第一个可用 Agent 的完整过程展开包括安装、配置、写 Skill、调通局域网访问以及我后来踩进去又爬出来的几个坑。适合两类人看一是想快速跑通一个 Agent Demo 但不想一上来就啃源码的开发者二是已经接触过 Agent 概念、但对“Harness 这种执行框架到底解决了什么问题”还没形成直观体感的学习者。1. 先弄清楚DeepSeek Harness 在 Agent 体系里到底处于什么位置1.1 为什么直接调大模型 API 不算“搭 Agent”很多人对 Agent 的第一个误解是只要接上大模型的 API能对话就算 Agent 了。但实际跑过一个稍复杂任务你就会发现纯 API 调用只能处理“你问一句、模型答一句”的静态交互。一旦任务变成“帮我读取本目录下所有 Markdown 文件提取其中的待办事项按优先级排序后生成一份新文档”模型本身是做不到的——它没有手不能遍历文件系统也不能执行写入操作。这时候你需要一套机制让模型在推理过程中主动决定“我要调用某个工具”然后由程序去执行这个工具再把结果喂回给模型让它基于结果继续推理。这个“推理—行动—观察”的循环ReAct 模式才是 Agent 的核心而 DeepSeek Harness 这类执行框架就是为了把这个循环封装成开箱即用的基础设施。1.2 Harness 与模型的边界划分我用了一张很朴素的图来理解它在实际开发中我更喜欢直接在脑子里建立这个模型Harness 是“身体”模型是“大脑”。大脑负责思考下一步做什么身体负责真正动手。DeepSeek Harness 做的事情就是把“身体”的各种能力——工具注册、参数校验、上下文窗口管理、多轮对话的状态保持、甚至是调用哪个模型版本——统一管理起来。开发者在配置里声明好模型接入方式再按规定的接口格式写出几个工具函数剩下的循环控制逻辑根本不用自己操心。这一点和 Codex Harness 的思路本质上是一致的与其在应用层一次次手写“把系统提示词拼进去、把工具返回结果拼进去、再发给模型”这种重复代码不如让框架把这个回合制流程标准化。1.3 它擅长的事与不适合的事我用了一周时间做了几组对比测试简单总结一下它的能力边界。擅长的事快速搭建个人知识库问答 Agent、让 Agent 操作本地文件完成文档整理、写一个能自动查天气/查时间的工具型 Agent、在局域网内做服务化部署供多设备调用。不适合的事需要复杂人工审核流的生产级业务系统比如涉及多人审批、权限分级的场景、需要大规模分布式并行任务调度的场景、以及对延迟极其敏感的实时交互场景。它不是万能的业务中间件而是一个“把模型变成一个能干活的执行体”的轻量框架。想清楚这一点你就不会在错误的方向上浪费时间。2. 安装部署从环境准备到跑通 Hello World2.1 运行环境与前置依赖先说结论一台能联网的电脑即可官方推荐的运行环境是 Python 3.10 以上版本我实际测试时用的是 Windows 11 WSL2 Ubuntu 22.04后来又在一台纯 Linux 服务器上跑了一遍都没有问题。安装之前需要确认三件事第一本机 Python 版本是否达标python --version第二是否有可用的 DeepSeek API Key目前我主要用官方 API 接入方式如果你打算走本地模型路线比如通过 Ollama 暴露本地接口Harness 也支持自定义 OpenAI 兼容的 Base URL第三网络环境能否正常访问 API 端点。# 验证 Python 版本 python --version # 建议在虚拟环境中安装避免污染全局环境 python -m venv harness_env source harness_env/bin/activate # Windows 下执行 harness_env\Scripts\activate2.2 安装命令与常见报错安装过程本身非常简单核心就一条命令pip install deepseek-harness但如果你是第一次装大概率会遇到几个小问题。我在根目录直接装的时候提示pip版本过低先升级一下就行另外这个包对pydantic版本有要求如果你的项目里已经有旧版 pydantic很可能会出现版本冲突。我的建议是强烈优先用虚拟环境不要直接往全局环境里塞依赖。还有一个小细节国内网络环境下有时候直接 pip 安装会比较慢甚至超时可以临时换用国内镜像源安装速度会快很多。# 换用国内 PyPI 镜像 pip install deepseek-harness -i https://pypi.tuna.tsinghua.edu.cn/simple安装完成后验证一下python -c from harness import Agent; print(ok)如果没有任何报错说明框架已经装好了。我第一次验证的时候在这里卡了很久一直提示ModuleNotFoundError: No module named harness排查了半天发现是虚拟环境没有激活就直接运行了 Python这种小问题新手真的很容易遇到。2.3 初始化配置API Key 与模型选择DeepSeek Harness 的配置思路是“约定优于配置”。首次启动时它会自动在工作目录生成一个harness_config.yaml这个文件就是整个 Agent 的中枢配置。我当时打开看了一眼核心配置项非常直观model: provider: deepseek api_key_env: DEEPSEEK_API_KEY model_name: deepseek-chat temperature: 0.7 max_tokens: 4096 agent: name: my-first-agent system_prompt: 你是一个乐于助人的 AI 助手可以调用工具完成用户的任务。 max_iterations: 10 memory: max_messages: 50 type: sliding_window server: host: 127.0.0.1 port: 8080这里有几个关键设计值得展开说说。api_key_env的意思是框架不会让你把 Key 直接写进配置文件而是读取环境变量DEEPSEEK_API_KEY这个设计比硬编码安全得多万一以后要把配置文件分享出去也放心。max_iterations是 Agent 在放弃前最多执行多少轮“思考—调用工具—观察结果”的循环我一开始默认没在意这个值后来跑复杂任务时发现默认值偏低Agent 经常在任务中途就被截断调大之后顺畅多了。memory部分控制的是多轮对话的记忆机制sliding_window类型意味着超出窗口的旧消息会被丢弃这对控制 token 成本很有用。2.4 跑通第一个 Hello World配置改好后我写了一段最简单的启动逻辑from harness import Agent agent Agent() result agent.run(你好请介绍一下你自己) print(result)运行后终端的输出非常有意思——它不是直接把答案甩给你而是先把整个思考过程亮出来包括“我正在分析用户意图‘自我介绍’这不需要调用工具直接基于模型知识回答”然后才输出最终回复。这个过程让我第一次真切感受到Harness 在把 Agent 的“思维链”透明化。对调试来说这个体验极好你能清楚地知道模型每一步都在干什么而不用靠猜。3. Agent 的运行逻辑拆解那一整套循环是怎么转起来的3.1 模型决策与工具执行的回合制很多人第一次接触 Agent最容易困惑的一个问题是模型怎么知道什么时候该调用工具、调用哪个工具答案其实不神秘——核心机制是函数调用Function Calling。当你给模型声明了一批结构化工具描述之后模型在推理时会自己判断“这一步是否需要工具介入”如果需要它不会直接去执行工具而是输出一个格式化的“调用请求”比如get_current_weather(location北京)。Harness 拿到这个请求后去注册表里找到对应的函数用你传入的参数实际执行它再把执行结果包括成功数据和可能的报错信息作为一条新的消息返回给模型。模型看完结果后继续推理决定是再调下一个工具还是给出最终答案。整个过程就是这样一个循环直到模型认为任务完成或者达到步数上限。3.2 Harness 在循环中替你做了什么如果你自己写过一遍这个循环就会知道模型调用不是最麻烦的麻烦的是每一步的状态管理。比如上一轮工具返回了一个很长的 JSON下一轮模型需要基于它继续推理你得把这条结果塞进上下文再比如模型偶尔会“犯浑”连续给出格式错误的工具调用请求你得做容错和重试。这些脏活Harness 全都自动处理了。以我实际跟踪到的日志为例[1] LLM 响应: tool_call get_files_in_directory(path/home/user/projects) [2] 执行工具 get_files_in_directory - 返回 3 个文件 [3] 将工具结果注入上下文 - 继续调用 LLM [4] LLM 响应: tool_call read_markdown_file(path/home/user/projects/README.md) [5] 执行工具 read_markdown_file - 返回 2841 字符 [6] 将工具结果注入上下文 - 继续调用 LLM [7] LLM 响应: 直接回答用户问题整个过程你只管看日志框架会自动把工具调用产生的临时结果追加到消息列表里并保证这些消息格式能被模型正常读取。这种透明化调度让 Agent 的排错体验好了一个数量级。3.3 为什么需要max_iterations这个保险丝我在 2.3 节提到过max_iterations这里再深入说一句。没有这个上限Agent 理论上会一直循环下去遇到一个永远无法收敛的任务时它会反复调用工具、反复得到相同的结果、再反复调用直到把上下文窗口撑爆或者花掉你一大笔 token 费用。这就像给一个执着的电脑程序配了一个紧急断电按钮。我后来用 Harness 跑一个“全库搜索某个关键词并总结”的任务时由于没有限好迭代次数日志里出现了连续七八次的相似工具调用当时就意识到这个参数的重要了。建议所有新手先把max_iterations调到 5-8 之间跑通了再往大的调。4. 从零到一构建一个能读文件并总结的实用 Agent4.1 第一个 Skill让 Agent 拥有“读 Markdown 文件”的能力如果你只是让 Agent 聊聊天那它还配不上“Agent”这个名字。真正的转折点发生在你给它一个工具在 Harness 里工具通常以 Skill 的形式定义。我做的第一个 Skill 非常简单——读取指定路径的 Markdown 文件内容。这个功能看着基础但几乎是所有本地知识库 Agent 的基石。需要新建一个skills目录然后在里面建一个子目录file_reader并创建两个文件SKILL.md和skill.py。SKILL.md是技能说明文件主要给模型看的--- name: file_reader description: 读取本地文件系统中的 Markdown 文件内容返回纯文本。 parameters: file_path: type: string description: 要读取的文件绝对路径例如 /home/user/docs/readme.md --- 该工具用于读取 Markdown 格式的本地文件读取结果将作为模型推理的参考依据。skill.py是实际执行逻辑from pathlib import Path def run(file_path: str) - str: 读取 Markdown 文件并返回内容 path Path(file_path) if not path.exists(): return f错误文件 {file_path} 不存在请检查路径 if path.suffix.lower() ! .md: return f错误仅支持读取 .md 文件你提供的是 {path.suffix} content path.read_text(encodingutf-8) return f文件内容如下共 {len(content)} 字符\n{content[:3000]}注意我在这里做了两个防御性处理一是检查文件是否存在二是限定了扩展名。这很重要——模型并不“理解”文件系统它只是按理解生成参数如果不对参数做边界校验遇到一个不存在的路径时工具会抛异常整个循环就会卡住。另外我特意限制返回内容长度不超过 3000 字符目的很明确防止大文件内容一次性塞爆上下文窗口。4.2 编写带状态记忆的 Skill实现多轮对话中的“记住待办”第一个 Skill 是纯函数式的接下来我尝试了一个带状态记忆的 Skill用的场景是“让 Agent 帮忙管理待办事项”。待办管理的难点在于它需要跨轮对话保持状态第一轮用户说“添加买菜和写周报”第二轮用户问“我现在有哪些待办”Agent 必须记住之前的内容。我的实现方案是通过一个本地 JSON 文件来保存状态import json from pathlib import Path TODO_FILE Path.home() / .harness_todo.json def run(action: str, content: str ): 管理待办清单支持 add/list/complete 三种操作 if not TODO_FILE.exists(): TODO_FILE.write_text(json.dumps({todos: []}), encodingutf-8) data json.loads(TODO_FILE.read_text(encodingutf-8)) if action add: data[todos].append({item: content, done: False}) TODO_FILE.write_text(json.dumps(data, ensure_asciiFalse), encodingutf-8) return f已添加待办{content} elif action list: if not data[todos]: return 当前没有待办事项 return \n.join( f{[x] if t[done] else [ ]} {t[item]} for t in data[todos] ) elif action complete: for t in data[todos]: if t[item] content: t[done] True TODO_FILE.write_text(json.dumps(data, ensure_asciiFalse), encodingutf-8) return f已完成{content} return f没有找到待办{content} return 未知操作仅支持 add/list/complete这种“用文件保存状态”的方式优点是简单可靠、任何重启都不会丢数据。如果你以后想换成数据库原理也是一样的——Skill 内部保持无状态外部存储负责持久化。这里有个容易忽略的细节我把ensure_asciiFalse加上了否则中文内容写入 JSON 时会变成\uXXXX转义序列读出来虽然能还原但你在命令行里直接看文件内容时会很不直观。4.3 组装与验证让 Agent 自己决定调用哪个 SkillSkill 定义好后还需要让它被 Harness 感知。我最初以为要改配置文件后来发现框架有自动扫描机制——只要把 Skill 放在正确的目录结构里启动时它会自动读取SKILL.md的描述注册成可用的工具。之后我在配置文件里把skills_dir指向对应目录skills: dir: ./skills auto_scan: true然后问 Agent“请读取 /Users/me/notes/ideas.md然后把里面的核心观点提炼成三条待办事项。”它在没有人工干预的情况下自动完成了一次完整的工具调用链先调用file_reader读取文件看到内容后判断这是文档中的行动类信息再调用todo_manager把内容逐条加进待办清单。看着日志里两次工具调用依次完成我还是有点小激动的——这就是 Agent 区别于普通聊天机器人的本质它不是为了回答问题而是为了完成任务。5. 进阶部署局域网访问与服务化5.1 让 Agent 跑成服务而不只是脚本如果你的 Agent 只在自己电脑上跑前面 4 节已经够了。但很多时候我们希望它能被局域网里的手机、另一台电脑或者后续要开发的前端应用调用这时候就要把 Agent 启动成 HTTP 服务。Harness 的server模块刚好提供这个能力。我是在配置好harness_config.yaml之后用一行命令启动的python -m harness.server这个命令会加载当前目录的配置启动一个基于 FastAPI 的本地服务。默认监听127.0.0.1:8080这种情况下只能本机访问。想要局域网内其他设备访问需要改两个地方。5.2 局域网访问配置host 与端口局域网访问本质上就两步把监听地址从127.0.0.1改成0.0.0.0然后确保防火墙放行对应端口。深挖一层你会发现0.0.0.0的含义是“监听本机所有网络接口”这样局域网内其他设备才能通过你的机器 IP 访问到服务。server: host: 0.0.0.0 port: 8080改完配置重启服务然后在同一局域网的另一台设备浏览器里访问http://你的电脑局域网IP:8080/docs看到 API 文档页面就说明服务通了。我在这里踩过一次坑改完配置后一直访问不通排查半天发现是 Ubuntu 的 ufw 防火墙没有放行 8080 端口。解决办法很简单sudo ufw allow 8080/tcp如果是 Windows需要在“防火墙高级设置”里新建入站规则放行 8080 端口。这个步骤看起来基础但真的很容易漏。5.3 鉴权问题千万别裸奔在一个不信任的局域网里把 Agent 服务暴露到局域网有一个必须重视的问题默认的服务没有任何鉴权凡是能访问到你 IP 的人都能用你的模型 API Key 消耗 token。我们的个人项目通常不需要特别复杂的身份验证但至少要加一层最简单的 Token 校验。我用的是在启动命令后追加一个外部 API 网关比如 nginx做 Basic Auth 的方式配置大致如下location / { proxy_pass http://127.0.0.1:8080; auth_basic Agent Access; auth_basic_user_file /etc/nginx/.htpasswd; }这样即便别人扫描到你的端口没有用户名密码也调不了接口。个人项目够用了。往深一步想局域网部署其实对网络架构理解的要求比 Agent 本身高但反过来说搞定这一层你就已经把一个“本地脚本”升级成了“真正的服务”。6. 踩坑实录从安装到跑通那些百度不到答案的问题6.1 跨平台路径分隔符问题这个问题藏得很深。我在 Windows 本机定义 Skill 时file_reader接收到的file_path参数是C:\Users\me\notes\ideas.md但在 WSL2 环境里跑的时候路径变成/home/me/notes/ideas.md。直接硬编码路径或者用默认分隔符拼接字符串都会导致路径解析失败。最稳妥的方案是不要用字符串拼接路径而是交给pathlib.Path去处理它会根据当前操作系统自动选择正确的分隔符。如果你需要在配置里写默认路径也建议写成相对路径运行时再转绝对路径。这个细节不致命但很磨人。6.2 模型频繁返回“找不到可用工具”这是一个非常典型的 Agent 新手问题明明 Skill 注册成功了但模型始终回复“我无法完成这个操作因为我没有相关工具”。根本原因大概率是SKILL.md里的描述不够清晰。模型选择工具靠的是语义匹配如果你的描述里写的是“读取文件”而用户的问题用的是“查看一下这个文档”模型可能匹配不上。解决方法是把描述写得更宽泛一些description: 读取或查看本地 Markdown 文件内容适用于用户提到读取文件查看文档打开笔记分析内容等场景。加完这一段描述后这个 Skill 的触发率明显变高了。这里也侧面说明了你在 Agent 开发里做的很多工作不是“写代码”而是“翻译”——把人类的模糊意图翻译成模型能准确理解的工具语义。6.3 工具返回大量数据导致上下文爆炸这是当前 Agent 开发里最容易被低估的问题。第一次用file_reader读取一个长文档时我的日志显示上下文使用量急剧上升一次调用就吃掉了几千 token如果连续读取三四个文件上下文窗口直接告警。解决问题的思路不是买更大的窗口而是在工具返回时就做裁剪。我在 4.1 节中已经把返回值截断到 3000 字符这是一个保守而有效的值。在后续实战中我的处理逻辑可以总结成一条原则工具返回给模型的内容只保留与当前任务最相关的部分其余全部过滤掉。如果你想更精细控制甚至可以返回结构化摘要而不是原始全文。这个习惯越早养成越好否则 Agent 的可用性和成本完全不可控。6.4 服务启动后宿主机能访问、局域网设备访问不了这个问题排查起来比较全面。我的经历是宿主机curl http://localhost:8080一切正常但手机访问http://192.168.x.x:8080一直超时。排查链路如下——先确认监听地址是否确实是0.0.0.0在服务启动日志里能看到Uvicorn running on http://0.0.0.0:8080再用另一台机器nc -vz 192.168.x.x 8080测试端口通不通最后检查防火墙。其实还有一个冷门但常见的原因Windows 网络配置文件如果是“公用网络”就算你在防火墙里加了放行规则也会被默认阻止入站连接需要把网络配置文件改成“专用网络”。现象可能原因解决方式宿主机访问正常局域网超时监听地址仍是 127.0.0.1改成0.0.0.0并重启端口测试不通防火墙拦截入站ufw allow 8080/tcp或 Windows 新建入站规则防火墙已放行仍不通网络类型为公用网络改成专用网络重新测试7. 从 DeepSeek Harness 出发AI Agent 开发的下一站7.1 学习路线先跑框架再啃原理在这篇文章的最后一部分我想聊聊学习路线的问题。很多人一上来就啃 LangGraph 源码、研究多 Agent 协作范式结果被各种抽象概念劝退。我的建议恰恰相反先用一个开箱即用的 Harness 把第一个 Agent 跑起来建立体感再回头研究原理。体感是什么是你亲自看到模型在“思考—调用工具—查看结果—再思考”这个循环里转起来是你亲手写了一个 Skill 然后被模型准确调用时的成就感。有了这个基础你再去看 LangGraph 的状态图设计、MCPModel Context Protocol 的标准接口、或者更复杂的多 Agent 协作框架会轻松得多因为你已经知道这些工具在解决哪些具体的问题了。7.2 下一步接入知识库与 MCP 生态个人知识库是目前 Agent 应用中最热门的方向之一。我在跑通文件读取 Skill 之后很自然地就把它往 Obsidian 这个方向延伸了——让 Agent 直接读取 Obsidian 仓库里的所有.md笔记然后针对这些笔记内容做问答。这样做的好处是Agent 不需要提前“训练”你的笔记它每次回答时实时去你的仓库里找相关内容相当于用模型做搜索引擎式的推理问答。如果希望这个能力变成标准化对接可以去了解 MCP 协议。Harness 目前支持通过 MCP 连接外部资源这意味着理论上你能接上各种现成的 MCP Server让 Agent 获得访问数据库、操作浏览器、甚至连接设计工具的能力。MCP 的价值在于它制定了一套统一接口让 Agent 开发从“每个工具都要自己造轮子”走向“工具即插即用”的阶段。7.3 一个人开发 Agent 项目的效率心得最后说一点工具之外的心得。Agent 开发有一个很独特的特性它的工作流不是“写代码—编译—运行—看结果”而是“写配置—写描述—运行—观察行为—调描述”。也就是说很多时候你在调整的不是逻辑本身而是模型对工具的理解方式。这决定了你的调试习惯必须改变少用断点多开日志少问“为什么这段代码报错”多问“模型为什么在这个环节做出了错误判断”。DeepSeek Harness 的日志机制很透明每次工具调用的前后文都记录得清清楚楚这在实际排错时帮了大忙。我个人在实际操作中还有一个体会第一版 Agent 不要贪多一个 Skill、一类任务就够了。我见过太多人第一天就想做一个全能助理结果百分之八十的时间耗在调试各种工具的相互干扰上。先让一个简单的闭环稳定跑起来再逐步往上面加东西这条路线对 Agent 项目的成功率影响远超你想象。从 “能跑” 到 “稳定跑”中间隔着的不是模型选择而是你对这个循环的理解深度。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。