我本来只是想把 DeepSeek 接进一个 Agent让它帮我整理文档、画几张图、自动写周报。结果调试了一周之后这个 Agent 被我扔进了网络安全巡检的活儿里而且成了我目前觉得落地最顺的场景之一。这篇文章不聊空概念只聊我的真实路径DeepSeek 在 Agent 开发里到底怎么用Agent 框架、工具调用、函数返回这些坑是怎么踩出来的以及为什么一个通用 Agent 最后会干起网络安全这种听起来非常垂直的活儿。如果你想复刻这条路线本文会把能直接抄作业的部分都摆出来包括 API 调用方式、工具函数定义、循环执行逻辑和一些排错经验。1. DeepSeek 为什么成了 Agent 开发的首选底座1.1 从“聊天”到“工具调用”大模型角色的转变先讲清楚一个概念Agent 和大模型的边界不在“模型会不会写诗”而在“模型会不会调用工具”。普通聊天是你问一句、它答一句Agent 是模型先判断“这个问题该用哪个函数”然后程序的执行器去调用真实服务再把结果塞回给模型最后模型基于真实数据给出结论。DeepSeek 在这类 Agent 工作流里特别受青睐原因是三个字性价比高。它的 API 兼容 OpenAI 格式也就是说你只要改一下 base_url 和 api_key很多现成的 Agent 代码可以直接跑通。function calling 能力也够用对于大多数工具调用场景非常稳定。很多人一开始拿它做文档总结、画图、写脚本做着做着发现它最擅长的其实是处理大量结构化数据、调用外部接口、逐步排查问题——这恰好就是安全运营干的事。我个人的体感是DeepSeek 的模型在“理解工具描述”上表现不错但它不是所有时候都听话。如果工具描述写得不清楚它也会瞎猜参数。所以后续所有 Agent 项目的成败往往不是模型强不强而是你给它的工具说明精不精准。1.2 十分钟跑通第一个 DeepSeek Agent我假设你已经注册了开放平台、拿到了 API key。下面用 Python 的 openai 库来演示因为 DeepSeek 的接口兼容这个协议。from openai import OpenAI client OpenAI( base_urlhttps://api.deepseek.com, api_keysk-你的key ) tools [ { type: function, function: { name: get_weather, description: 获取指定城市的当前天气, parameters: { type: object, properties: { city: {type: string, description: 城市名如北京} }, required: [city] } } } ] messages [{role: user, content: 北京今天天气怎么样}] resp client.chat.completions.create( modeldeepseek-chat, messagesmessages, toolstools, tool_choiceauto )这里的关键不是把对话发出去而是看响应里的tool_calls。模型不会真的去查询天气它只会返回“我想调用 get_weather参数是{city: 北京}”。真正干活的是你自己写的那个get_weather函数或者你封装的外部服务。执行完函数之后需要把结果以tool角色的消息追加回会话再让模型生成最终自然语言回答。这个“模型提议 → 程序执行 → 结果回填 → 模型总结”的循环就是 Agent 的最小闭环。注意如果模型返回了工具调用但你忘了把执行结果作为 typetool 的消息传回去API 会直接报类似 “messages tool calls need immediate results” 的错误。这不是 DeepSeek 的问题而是协议要求工具调用的结果必须紧跟在下一次请求里。1.3 什么时候该本地部署 DeepSeek很多安全场景都有数据合规要求比如客户日志、内网资产清单不应该出内网。这时候就必须把 DeepSeek 拉到本地跑。常见方案是 Ollama 拉量化模型或者用 vLLM 做高性能推理。本地部署最大的好处是数据不出机房租用环境但代价是模型能力会弱一些尤其对工具调用的指令理解会不如大参数的在线版本。我的经验是如果只是刚学习直接调 API 最合适如果已经明确要部署到客户的隔离网络里再研究 Ollama 或 vLLM。不要一上来就本地部署因为 Agent 调试最耗时间的不是推理速度而是工具返回格式是否正确。等在线 API 把逻辑验证好了再切本地模型会省很多事。2. 一堆新词harness、hermes、agent、skill 到底怎么区分2.1 别被社区造词搞晕最近社区里出现了大量和 DeepSeek 相关的 Agent 项目名词比如 DeepSeek harness、DeepSeek Hermes、PI Agent还有很多人问 harness 和 agent 有什么区别、skill 和 agent 有什么区别。我按自己的理解做个区分Agent是一个能够自主决策并调用工具的智能体。它由大模型担任“大脑”由外部函数担任“手脚”。Harness直译是“挽具”在 AI 圈里可以理解成“给 Agent 提供的运行环境和配套工具集”。它负责管理模型循环、工具注册、内存、日志等基础设施。Skill是 Agent 可以复用的一项具体能力比如“生成巡检报告”“解析 Nmap 输出”“调用威胁情报接口”。Skill 是原子能力Agent 是调度这些能力的决策者。Hermes在开源社区里常指一类经过微调的模型系列比如基于 DeepSeek 做角色行为和指令遵循优化的版本。它不是框架更多是“行为风格”或“微调产物”。所以当你看到 “DeepSeek harness” 这个词时不要以为它是什么官方标准。它只是社区对“DeepSeek 驱动的 Agent 运行环境”的一种叫法。看项目是否靠谱主要看它能不能跑通真实的工具调用循环而不是看名字。2.2 Agent 框架怎么选不同框架解决不同问题选型上我按场景分了几类框架适合场景优点缺点LangChain / LangGraph复杂流程编排、多步骤工具调用生态完善、可控性强学习成本高、抽象层多AutoGPT快速验证想法开箱即用容易失控生产环境不建议Dify / Coze快速搭产品、给业务方用界面友好定制能力受限自己写的循环只有两三个工具、场景固定调试简单、无黑盒不能应对复杂编排我的建议是如果你只需要“让模型调用一个扫描器、再调一个漏洞库、最后写报告”完全没必要上重型框架。自己写一个 30 行的循环反而最清晰。当你需要多个并发 Agent、条件分支、状态回滚时再考虑用 LangGraph 这类带图的编排框架。框架与编排是两个维度框架管积木编排管流程。2.3 吴恩达的 Agent 教程为什么值得看吴恩达有一门 Agent 相关的教程核心观点就是 Agent 的四个关键能力反思、规划、工具使用、多智能体协作。很多人刚接触时都会盯着“规划”和“反思”我却认为新手最应该优先吃透的其实是“工具使用”。原因很简单没有工具调用的 Agent 只是高级聊天框有了工具调用模型才能真正改变系统状态、读取真实数据。你可以在网上搜到大量“吴恩达 agent 教程”的笔记但真正要落地还是要回到自己和 DeepSeek 的 API 交互里。看十个教程不如自己跑通一个带工具调用的循环。2.4 用 Codex 接 DeepSeek 来写 Agent 代码很多人问 Codex 怎么接入 DeepSeek。其实因为 DeepSeek 兼容 OpenAI 接口你只需要把相关工具的模型配置指向 DeepSeek 的 base_url再把模型名改成 deepseek-chat就能用对话式编程工具帮你生成 Agent 代码。听起来好像很绕但实际链路是我用编程工具辅助写 Agent 的框架代码而这个 Agent 底层的推理又跑在 DeepSeek 上。等于说 AI 在帮我写一个由 AI 驱动的工具。这么做的好处是开发效率高坏处是生成代码的质量取决于模型对上下文的理解程度所以我从不让它直接生成完整的安全工具而是让它生成工具调用框架再由我填充具体命令。3. 工具调用是 Agent 的“命门”一次完整实操3.1 Agent 的工作循环我用一句话总结 Agent 的工作循环读取任务 → 模型判断需要哪个工具 → 返回工具调用参数 → 程序执行工具 → 把结果回填给模型 → 模型判断是否继续或给出结论。这个循环看起来简单但真正跑起来后你会发现所有坑都藏在“结果回填”这一步。比如工具结果太长把上下文撑爆比如工具返回的是 JSON但模型解析失败比如函数执行抛了异常框架直接输出一句 “Agent execution terminated due to error.”。这时候新手最容易懵因为你不知道是模型错了、函数错了还是上下文断了。我的习惯是永远先打印 traceback再检查工具函数的入参类型最后看消息序列是否完整。很多时候问题不是模型蠢而是你在上一轮调用之后没有把工具结果正确传回去。3.2 给 Agent 装“技能包”示例我写过一个很简单的示例给 Agent 配了三个技能获取公网 IP、获取 HTTP 响应头、生成图表。工具函数本质就是普通 Python 函数关键在于 JSON Schema 描述要写得足够清楚。def get_http_headers(url: str) - dict: import requests resp requests.get(url, timeout5, allow_redirectsTrue) return dict(resp.headers) def make_report(data: list, output_path: str) - str: with open(output_path, w, encodingutf-8) as f: f.write(str(data)) return output_path把这两个函数注册到 tools 数组里再给 DeepSeek 一个任务“检查某个 URL 的响应头并保存报告”。模型就会先调用 get_http_headers看到结果后可能再调用 make_report最后告诉你报告保存到哪里。这里有个常见误区Agent 画图不是说模型自己画而是模型决定调一个绘图工具、生成参数再由工具出图。所以“Agent 画图”至少需要两个函数一个是语义理解一个是真实渲染。3.3 错误信息到底是谁报的很多框架在 Agent 进程异常退出时会打印 “Agent execution terminated due to error.”。这个提示并不能告诉你真正原因它只是说“执行到这里挂了”。我见过最坑的情况是工具函数名写错了模型按旧名字调用框架找不到函数然后直接终止。排查方法如下先打开详细日志找到第一个异常堆栈。确认模型返回的 tool_calls 里的函数名是否和你注册的完全一致。确认参数类型是否匹配比如模型传了字符串你却在函数里用列表方法。如果工具是远程 API确认不是网络或鉴权问题。还有一个容易被忽略的点工具执行时间过长超出框架的默认超时时间也会被当作错误终止。所以在给 Agent 配工具时一定要记得设置合理的超时并在工具内部做异常捕获返回可读的错误信息给模型而不是直接抛异常。3.4 给 Agent 一个“手刹”不管模型多聪明我都不建议让它无限制地自主执行所有动作。尤其当 Agent 被用在网络安全相关场景时一个错误的工具调用可能带来超出预期的后果。所以我在所有项目里都会做三件事限制最大循环次数比如最多调用 5 次工具。每次工具调用前程序先校验函数名和参数白名单外的一律拒绝。涉及命令执行的工具默认打印命令让用户确认而不是直接跑。这个“手刹”设计其实来自一次教训有一次模型把目标参数写错差点扫描到没有授权的地址。从那天起任何涉及真实请求的工具都必须经过二次确认。4. 通用 Agent 怎么就走到了网络安全4.1 网络安全运营就是一个巨大的“工具台”我最初做 Agent 时想的都是文档、报表、画图这类通用场景。但真正上手后发现通用场景的需求千奇百怪每个客户要的格式都不一样做得非常累。反倒是网络安全运营有一个特别明显的特征基础设施极其依赖 API。扫描器有 API资产管理有 API基线检查工具有 API漏洞库有专门的查询接口日志平台也有 API。你几乎可以把整个安全运营链路拆成几十个函数然后让 Agent 做调度和决策。网络安全基线检查就是一个最典型的例子检查一系列系统配置项是否满足安全基准满足则为通过不满足则为风险项。这种规则明确、结果结构化、需要大量比对的工作天然适合 Agent 自动完成。4.2 一条务实的网络安全学习路线很多人问我网络安全怎么学平台在哪里。我的观点是平台不是关键关键是顺序。我给新人的建议路线是网络基础TCP/IP、DNS、HTTP先能看懂数据流。系统基础Linux 常用命令、权限模型、服务和端口概念。常见漏洞原理比如 OWASP Top 10理解“为什么会有漏洞”。工具使用端口扫描、流量抓包、配置检查重点是理解输出含义。CTF 和靶场在完全授权的环境里练手。SRC 平台在厂商授权范围内做漏洞挖掘和报告提交。这套路线的核心不是背工具而是建立“授权、验证、复现、报告”的习惯。相比盲目刷博客我更建议配合 Agent 一起学让它帮你解释扫描结果、让它帮你整理 baseline 配置差异、让它自动生成报告草稿。网络安全加 Agent不一定是攻击反而是学习和合规巡检最好的训练场。4.3 安全行业本身也在为 Agent 做安全现在学术界和工业界都在讨论一个反向问题大模型 Agent 本身会不会被攻击我看到一个很有意思的方向叫 A-MemGuard定位是“面向大模型智能体记忆的主动防御框架”。它关注的是 Agent 的记忆可能被恶意内容污染导致后续决策被操纵。这个方向说明了一个趋势安全从业者已经把 Agent 当作新的保护对象。你训练一个 DeepSeek 驱动的 Agent 去收集信息它也可能被钓鱼、被注入、被误导。所以 Agent 和安全的关系是双向的Agent 可以做安全工具Agent 本身也需要安全防护。这也是很多安全论文里开始出现 Agent 的原因。大家逐渐意识到与其讨论通用 AI 能不能接管安全不如先把手头的 Agent 调试稳定再逐步扩大它负责的环节。4.4 我选择“安全巡检”而不是“攻击自动化”的原因标题问“最后怎么干起了网络安全”我得说清楚我干的是安全巡检、基线检查、报告生成这种防守型工作不是攻击自动化。原因有两个。第一是合规。没有授权的扫描是明确越界的这个红线不能碰。SRC 平台之所以安全是因为它给了你一个明确的范围和授权。我在所有演示环境里都会加一句话只对自有系统或虚拟环境操作。第二是技术现实。攻击自动化需要大量对抗性操作比如绕过检测、免杀、利用链构造这些对模型幻觉非常敏感。防守侧的任务反而更适合大模型信息收集、日志阅读、配置比对、报告产出。所以准确地说不是 Agent 干了攻击的活而是 Agent 接了安全运营里最繁琐的那一部分。5. 手把手搭建一个 DeepSeek 安全巡检 Agent5.1 场景设定我搭的 Demo 是这样的在本地虚拟机里运行一台 Web 服务我需要 Agent 帮我完成三步操作扫描这台虚拟机的常见端口。获取 Web 服务的 HTTP 响应头。把结果整理成一份 Markdown 巡检报告。所有操作都在我自己控制的网络环境里完成。如果你要复用请先确认目标系统是你的或者你已经拿到书面授权。不要拿别人家的域名直接跑。5.2 工具函数实现我定义了三个工具函数def run_port_scan(target: str) - dict: import subprocess result subprocess.run( [nmap, -sV, -p1-1000, target], capture_outputTrue, textTrue, timeout60 ) return {output: result.stdout} def fetch_http_headers(url: str) - dict: import requests resp requests.get(url, timeout5, verifyFalse) return {status: resp.status_code, headers: dict(resp.headers)} def generate_markdown_report(section_title: str, content: str) - str: return ## section_title \n\n content这里有个细节nmap 输出通常很大如果直接把全部 stdout 塞回给模型很快会把上下文占满。所以我实际使用时会先做一层摘要比如只保留端口状态和服务的版本信息而不是把扫描全过程都塞回去。这个思路叫“工具返回要精不要全”。5.3 Agent 主循环实现主循环我写得很简单没有引第三方框架def run_agent(task: str): messages [{role: user, content: task}] max_iterations 5 for _ in range(max_iterations): resp client.chat.completions.create( modeldeepseek-chat, messagesmessages, toolstools, tool_choiceauto ) msg resp.choices[0].message messages.append(msg.model_dump()) if not msg.tool_calls: return msg.content for call in msg.tool_calls: name call.function.name args json.loads(call.function.arguments) if name run_port_scan: result run_port_scan(**args) elif name fetch_http_headers: result fetch_http_headers(**args) else: result {error: unknown tool} messages.append({ role: tool, tool_call_id: call.id, content: json.dumps(result, ensure_asciiFalse) }) return 达到最大迭代次数已停止。这个循环的逻辑就是 3.1 里说的最小闭环。限制最大迭代次数是一种保护防止模型在同一个工具上反复横跳。5.4 一次真实运行日志我跑了一次任务输入是“对 192.168.1.100 做常见端口扫描获取 8000 端口的 HTTP 响应头最后给出巡检报告。”日志大致是这样user: 对 192.168.1.100 做常见端口扫描... model: tool_calls: run_port_scan({target: 192.168.1.100}) tool: 22端口 open, 80端口 open, 8000端口 open model: tool_calls: fetch_http_headers({url: http://192.168.1.100:8000}) tool: status200, Server: nginx/1.18.0 model: 最终生成报告从日志能看出模型没有一次性把所有动作做完而是每拿到新的工具结果再决定下一步。这就是 Agent 和普通提示词不一样的地方它会在上下文里逐步累积证据最终再写结论。5.5 运行中的两个典型报错我在调试时遇到过两个出现频率很高的报错。第一个是 JSON 解析失败模型返回了类似{target: 192.168.1.100这种不完整的 JSON。解决办法是不要直接 json.loads而是先用正则把函数参数部分截出来或者用response_format{type: json_object}尽量保证输出合法。第二个是上下文超长。nmap 输出再长一点模型就会开始丢前面的信息。我的处理方式是在工具内部做截断只保留前二十条开放端口其余写入文件再把文件路径告诉模型。文件路径本身也是一个有效的工具返回结果。6. 避坑清单我给 DeepSeek Agent 踩过的 6 个坑6.1 工具调用结果忘返回现象是模型已经决定调用 get_weather但程序没有把执行结果加到 messages 里然后 API 报错提示信息里带有 “tool calls need immediate results”。原因就是协议要求工具结果必须紧跟其后。解决办法是严格按照 protocol 把每条 tool_call_id 对应一条 tool 消息。6.2 工具结果太大导致上下文爆炸这个坑几乎每个人都会踩。模型拿到一个超长端口列表还没等它总结上下文已经超限。解决办法不是换更大的上下文而是让工具函数先做聚合只把最重要的字段返回给模型。原始数据可以写到外部文件让模型知道路径即可。6.3 Agent 循环停不下来如果你的 Agent 在一个工具上反复调用比如连续三次调用同一个查询接口说明模型“觉得”自己还需要更多数据但实际上信息并没有更新。解决方式是设置最大迭代次数同时检测重复调用。如果同一个工具和参数出现两次就直接终止并把已经收集到的东西强制输出成报告。6.4 模型幻觉生成不存在的工具模型有时会凭空编一个函数名比如我想让它调 scan_port它输出 call_system_command。如果框架没有对函数名做白名单校验这个调用就可能直接执行了一个危险操作。我在所有工具调用入口都会加一道校验if name not in allowed_tools: result {error: ftool {name} not allowed}这个习惯在安全场景里就是保命的。6.5 网络安全产品 API 的鉴权与限流安全产品往往有独立的管理后台API key 的权限范围要设置得很小。我建议给 Agent 单独建一个只读账号不要拿管理员 key 直接跑。同时它的 API 可能有限流Agent 并发调用时会触发 429。解决办法是在工具层做简单的重试和退避不要把所有压力直接打给内部平台。6.6 合规红线意识最后一条不算技术坑但它比技术问题更致命。Agent 一旦拥有工具调用能力它的每个动作都是真实发生的。没有授权的扫描、没有授权的数据访问都会造成严重后果。我的底线很明确所有联网类工具都标注“仅内网测试环境”所有扫描类命令都先打印目标地址所有自动化流程都保留详细审计日志。这些不是多余的步骤而是 Agent 能在安全行业待下去的基础。最后说点个人体会。把 Agent 当“实习生”而不是“全自动机器人”是目前最稳的使用心态。它不能独立完成整个安全运营但它能把最繁琐的信息收集、格式整理、基线对比干得又快又标准。DeepSeek 给这个方向提供了低成本入口Agent 框架负责约束它的行为边界而网络安全给了它大量可以真实调用的工具。我目前最推荐的跨界路径就是先用 DeepSeek 把巡检、报告、情报收集这类流程跑顺再逐步扩大它的权限范围。这不算什么预测这就是我自己跑下来最贴合实际的方式。