ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

DeepSeek Harness实战:批量评估与Codex接入排坑指南

DeepSeek Harness实战:批量评估与Codex接入排坑指南 之前在把 DeepSeek 接入 AI 编程工具链时经常看到一类很典型的报错本地转发服务处理 Codex 的/responses请求时返回 HTTP 400提示reasoning_content没有回传给 API。网上搜了一圈资料大多只讲“怎么配 Key”很少有人把 Harness 这类工程框架的底层原理、核心组件、批量评估逻辑和接入踩坑点完整串起来。本文就用一套闭环实战来把这些内容一次讲清从概念到环境搭建再到批量评测与 Codex 接入适合刚接触大模型工程化开发的新手也适合正在做模型评估、编码 Agent 接入的后端开发者。1. DeepSeek Harness 是什么先理解工程化位置1.1 一次接入报错背后的真实场景很多团队现在已经在用 OpenAI Codex CLI、Claude Code 这类终端编程 Agent 写代码。为了控制成本或满足数据合规要求社区里很常见的一种做法就是把底层模型切换成 DeepSeek通过本地转发服务把 Codex 的请求转换成 OpenAI 兼容格式再发给 DeepSeek API。在这个过程中只要开启了大模型的“思考模式”就很容易遇到上面的reasoning_content报错。为什么会出现这种问题因为 DeepSeek 的推理模型thinking mode返回结果时会额外带回一段推理过程下一轮请求时必须把这段内容原样传回去否则 API 会直接拒绝。这个问题不是 API Key 配错也不是网络不通而是“Harness”这一层工程框架没有处理好多轮上下文。换句话说当你开始做“模型接入”“批量评测”“Agent 工具调用”这些事情时你就已经从“调 API”阶段进入“大模型工程化开发”阶段而 Harness 正是在这个阶段发挥作用的。1.2 Harness 的直观理解Harness 英文原意是“马具、挽具”也可以理解为“测试台、装配架”。在大模型工程领域Harness 指的是把模型 API 包起来的一层工程框架它负责三件核心事情请求编排把一条 prompt 变成一批并发请求统一管理重试、超时、限流。结果采集把模型输出、token 消耗、延迟、推理过程等结构化地记录下来。质量评估用数据集、评测规则或另一个模型来给输出打分形成可对比的结论。你可以把 Harness 理解成一个“测评实验室”。没有 Harness 时你只能手动复制粘贴 prompt 到网页对话框里测试有了 Harness你可以一次性投喂几百条测试用例自动收集结果自动统计命中率还能在模型版本升级后做回归对比。1.3 Harness 与 Agent、CLI 工具的区别这三个概念经常混在一起但定位并不相同。Agent 是“智能体”它能自主拆解任务、调用工具、循环执行最终完成目标。CLI 工具是“客户端”比如 Codex CLI它封装了 Agent 逻辑让开发者在终端里与模型交互。Harness 则是“测试与约束环境”它可以承载 Agent也可以直接评测模型主要目标是让模型行为可控制、可观测、可对比。用一个类比来帮助理解Agent 是赛车手CLI 工具是赛车而 Harness 是测试跑道和计时系统。赛车手能不能跑得快一方面看车另一方面看跑道设计得是否科学。你在搞大模型工程化时真正要长期维护的恰恰是这条“跑道”。2. 环境准备与版本说明2.1 运行环境本文示例以通用开发环境为例不限定某一款操作系统操作系统Windows 10/11、macOS、Linux 均可。Python建议 3.10 或以上版本用于编写评测脚本、调用 API。Node.js如果你要使用 Codex CLI建议 18 或以上版本。包管理器pip、npm按实际环境安装。IDEVS Code 或任意你顺手的编辑器。版本需要根据你的项目实际情况调整本文重点演示配置思路不把版本号写死。2.2 获取 DeepSeek API Key要调用 DeepSeek 官方 API需要先在平台注册账号并创建一个 API Key。这里强调几个安全习惯API Key 属于敏感信息不要硬编码在代码里。推荐放到.env文件中并在.gitignore中忽略它。不要截图、不要提交到公开仓库。示例项目目录结构如下deepseek-harness-demo/ ├── .env ├── .gitignore ├── data/ │ └── eval_cases.json ├── output/ │ └── results.jsonl ├── run_eval.py └── requirements.txt2.3 模型选择与 OpenAI 兼容接口DeepSeek 官方 API 兼容 OpenAI 的对话补全接口。你只需要在 OpenAI SDK 中修改base_url和api_key就能直接调用。常用模型名以平台当前提供的为准社区中一般用官方主模型或推理模型两类分别对应对速度和思考能力要求不同的场景。如果你还想在本地部署模型也可以使用 Ollama、vLLM 等工具开启 OpenAI 兼容接口然后用同一套 Harness 脚本去调用区别只是base_url和模型名不同。3. 核心组件与底层原理拆解3.1 请求编排组件从单次调用到并发调度初学者调用 API 时通常只写一个 for 循环每次请求都等待返回。这在十来个用例时还能忍一旦用例量上升到几百上千条串行调用就会非常慢。请求编排组件要解决的就是并发、超时、重试三个问题。下面是一个使用ThreadPoolExecutor做并发调用的最小示例# 文件路径run_eval.py核心片段 import time from concurrent.futures import ThreadPoolExecutor, as_completed from openai import OpenAI client OpenAI( api_keysk-你的key, base_urlhttps://api.deepseek.com ) def call_one(prompt: str) - str: resp client.chat.completions.create( modeldeepseek-chat, messages[{role: user, content: prompt}], temperature0.2, max_tokens1024, ) return resp.choices[0].message.content or prompts [ 用一句话解释什么是数据库索引, Python 中列表和元组有什么区别, ] with ThreadPoolExecutor(max_workers2) as pool: futures [pool.submit(call_one, p) for p in prompts] for future in as_completed(futures): print(future.result())这里的关键点是max_workers。并发数并不是越大越好API 端通常有速率限制并发过高会触发限流反而增加大量重试。建议从 4 到 8 开始再根据实际返回码逐步调大。3.2 评估回放组件为什么需要 Golden Answer调用模型只是第一步工程化评测的核心是“可重复、可对比、可回归”。在 Harness 中我们通常会准备一组测试用例每条用例包含id、prompt、expected期望输出有的还会带model、temperature等参数。这组测试用例在行业里常被称为 Golden Set。有了 Golden Set 之后每次模型升级、Prompt 修改、参数调整都可以跑同一套用例然后把结果和上一次对比。这样你就能回答一个很实际的问题这次 Prompt 优化到底是变好了还是变差了如果只看一两个例子很容易被偶然性误导。3.3 日志追踪组件记录比输出更重要在 Harness 中日志追踪组件容易被忽略但它恰恰是排查问题的关键。一次完整的调用记录至少应该包含用例 ID 与 Prompt 内容模型输出内容推理过程内容如果开了 thinking mode输入 token 数、输出 token 数首字延迟、总延迟使用的模型名、参数是否重试、错误信息把这些信息按 JSONL 格式落盘后你就可以用脚本做统计也可以接入 OpenTelemetry、Langfuse 这类可观测平台做可视化分析。没有日志追踪遇到问题就只能靠“重新跑一遍碰运气”。3.4 DeepSeek 推理模型的 thinking mode 原理DeepSeek 的推理模型在生成正式回答前会先产生一段推理内容。调用普通对话模型时响应里只有message.content而调用推理模型时响应中会多出reasoning_content字段。这个字段代表模型的“思考链”。在多数情况下你可以选择把思考链展示给用户也可以选择忽略。但在多轮对话场景中问题就来了如果下一轮请求没有把上一轮 assistant 消息中的reasoning_content回传给 APIAPI 就可能在 thinking mode 下返回 400因为模型认为推理上下文不完整。这个机制看起来严苛但它是有意设计的推理模型的输入输出必须保持思维链连续否则模型无法在长任务中保持上下文一致性。Harness 在转发请求时必须把 assistant 消息拆成content和reasoning_content两部分并原样传给下一轮。接入 Codex 时很多本地转发层就是在这里出了问题。4. 实战一半小时跑通 DeepSeek Harness 批量评估4.1 创建项目与安装依赖先创建项目目录和虚拟环境mkdir deepseek-harness-demo cd deepseek-harness-demo python -m venv venv # Windows venv\Scripts\activate # macOS / Linux source venv/bin/activate安装依赖pip install openai python-dotenv把依赖写入requirements.txtopenai1.0.0 python-dotenv1.0.0接着创建.env文件DEEPSEEK_API_KEYsk-你的key DEEPSEEK_BASE_URLhttps://api.deepseek.com在.gitignore中忽略敏感文件.env venv/ __pycache__/ output/4.2 准备评测数据集创建data/eval_cases.json这里放入两条示例用例[ { id: 001, prompt: 用一句话解释什么是数据库索引, expected: 索引是数据库为了加速查询而建立的数据结构 }, { id: 002, prompt: Python 中列表和元组有什么区别, expected: 列表可变元组不可变 } ]实际项目中建议从线上真实用户问题、客服对话、历史 badcase 中收集用例。用例越贴近真实场景评测结论越有参考价值。4.3 编写批量评测脚本下面是一个完整的批量评测脚本。它读取 JSON 测试用例用线程池并发调用 DeepSeek API然后计算简单的包含匹配命中率并把结果写入 JSONL 文件。# 文件路径run_eval.py import json import os import time from concurrent.futures import ThreadPoolExecutor, as_completed from dotenv import load_dotenv from openai import OpenAI load_dotenv() client OpenAI( api_keyos.getenv(DEEPSEEK_API_KEY), base_urlos.getenv(DEEPSEEK_BASE_URL, https://api.deepseek.com), ) def load_cases(path: str) - list[dict]: with open(path, r, encodingutf-8) as f: return json.load(f) def call_model(case: dict) - dict: start time.time() resp client.chat.completions.create( modelcase.get(model, deepseek-chat), messages[{role: user, content: case[prompt]}], temperaturecase.get(temperature, 0.2), max_tokenscase.get(max_tokens, 1024), ) latency time.time() - start content resp.choices[0].message.content or usage getattr(resp, usage, None) return { id: case[id], prompt: case[prompt], expected: case.get(expected, ), output: content, latency: round(latency, 2), prompt_tokens: usage.prompt_tokens if usage else 0, completion_tokens: usage.completion_tokens if usage else 0, } def evaluate(result: dict) - dict: expected result.get(expected, ).strip() output result[output].strip() if expected: result[exact_match] expected in output or output in expected else: result[exact_match] None return result def main(): cases load_cases(data/eval_cases.json) results [] with ThreadPoolExecutor(max_workers4) as pool: futures [pool.submit(call_model, c) for c in cases] for future in as_completed(futures): raw future.result() results.append(evaluate(raw)) os.makedirs(output, exist_okTrue) with open(output/results.jsonl, w, encodingutf-8) as f: for r in results: f.write(json.dumps(r, ensure_asciiFalse) \n) total_latency sum(r[latency] for r in results) exact [r for r in results if r[exact_match]] print(f完成 {len(results)} 条用例) print(f总耗时: {total_latency:.2f}s, 平均单条: {total_latency / len(results):.2f}s) print(f精确命中: {len(exact)}/{len(results)}) if __name__ __main__: main()这段代码里有两个地方需要展开说明。第一为什么使用线程池而不是协程因为 OpenAI SDK 的网络请求本身是阻塞 IO用ThreadPoolExecutor最简单直观适合入门。如果后续要跑几千条用例可以考虑用asyncio或更专业的任务队列。第二为什么exact_match使用“包含”而不是“完全相等”因为大模型输出通常带有解释性文字完全相等几乎不可能。示例只是演示 Harness 的工作流程真实项目里应该使用更科学的评估方式比如规则匹配、关键词覆盖、LLM Judge 打分。4.4 运行与结果解读运行脚本python run_eval.py预期输出类似完成 2 条用例 总耗时: 3.42s, 平均单条: 1.71s 精确命中: 2/2打开output/results.jsonl可以看到结构化结果比如{id: 001, prompt: 用一句话解释什么是数据库索引, expected: 索引是数据库为了加速查询而建立的数据结构, output: 数据库索引是一种用于加速数据查询的数据结构。, latency: 1.55, prompt_tokens: 26, completion_tokens: 30, exact_match: true}有了这份 JSONL你就可以进一步写统计脚本把不同模型、不同参数的测试结果汇总成表格甚至接入图表平台做可视化展示。4.5 这个案例的工程化扩展方向上面的 demo 只完成了 Harness 最基础的能力。实际落地时建议往以下方向扩展将测试用例从 JSON 文件迁移到数据库或 YAML 配置方便团队协作维护。增加“基线版本”概念每次跑完自动和上一次结果做 diff。引入 LLM Judge用另一个模型为输出打分解决“包含匹配”太粗糙的问题。把脚本包装成命令行工具支持--model、--temperature、--max-workers等参数。接入 CI/CD在 Prompt 变更或模型版本升级时自动触发评测。5. 实战二把 DeepSeek 接入 Codex Harness5.1 Codex CLI 与 Harness 的关系Codex CLI 是 OpenAI 开源的终端编程 Agent它可以在终端里读取代码、执行命令、修改文件。它本身包含了 Agent 逻辑但模型不一定非要用 OpenAI 的。通过配置项Codex CLI 可以连接 OpenAI 兼容接口因此社区里用 DeepSeek 作为后端模型的方案非常流行。当你想在 Codex 中使用 DeepSeek 时有两种常见接入方式方式一直连模式。Codex 直接调用 DeepSeek 的 chat completions 接口适合不需要 thinking mode 的场景。方式二本地转发模式。通过一个本地服务把 Codex 的/responses协议转换成 DeepSeek 的 chat completions 格式适合需要使用推理模型或统一管理 Key 的场景。不管哪种方式核心都是把“模型提供方”配置到 Codex 的配置文件中。5.2 最小配置示例以 Codex CLI 为例在用户目录下找到或创建~/.codex/config.toml# 文件路径~/.codex/config.toml model deepseek-chat model_provider deepseek [model_providers.deepseek] name DeepSeek base_url https://api.deepseek.com/v1 env_key DEEPSEEK_API_KEY wire_api chat配置项说明modelCodex 默认使用的模型名这里设置为 DeepSeek 的对话模型。model_provider指定要使用哪个 provider 块。[model_providers.deepseek]定义一个名为 deepseek 的 provider。base_urlOpenAI 兼容接口地址。env_keyCodex 会从这个环境变量读取 API Key。wire_api协议类型chat表示走 chat completions 协议。注意Codex 的配置格式在不同版本间更新较快。如果你的版本不识别wire_api字段请以官方文档为准很多报错都源于版本字段不一致。5.3 高频报错reasoning_content 问题修复如果你在 Codex 中使用了推理模型并且通过本地转发服务接入大概率会碰到下面这类报错cc switch local proxy failed while handling codex endpoint /responses. provider: deepseek; model: deepseek-v4-flash; upstream_status: http 400; cause: the reasoning_content in the thinking mode must be passed back to the api.模型名以你实际配置为准这里关键是报错的最后一句话reasoning_content必须回传给 API。这个问题通常由三个原因叠加导致第一你配置了推理模型模型返回的 assistant 消息里带有reasoning_content。第二Codex 使用/responses协议与本地转发服务通信转发服务需要把 responses 协议转换成 chat completions 协议。第三转换过程中assistant 消息里的reasoning_content被丢弃了导致下一轮请求不满足 thinking mode 的上下文要求。解决方案有以下几种按推荐程度排序方案一升级本地转发服务到支持reasoning_content回传的版本。方案二暂时关闭 thinking mode把模型切到普通对话模型比如deepseek-chat。方案三调整 Codex 配置使用wire_api chat直连模式绕过/responses转换。方案四如果你自己维护转发服务手动把reasoning_content拼进下一轮请求。下面是一段自定义转发服务中构建 OpenAI messages 的示例思路核心片段如下# 示例思路按实际项目调整 def to_openai_messages(codex_messages: list[dict]) - list[dict]: messages [] for msg in codex_messages: if msg.get(role) assistant and reasoning_content in msg: messages.append({ role: assistant, content: msg.get(content, ), reasoning_content: msg.get(reasoning_content), }) else: messages.append({ role: msg[role], content: msg.get(content, ), }) return messages这段代码的重点在于assistant 消息不是只保留content而是把reasoning_content作为独立字段一起传给 DeepSeek API。实际接入时你需要确认 DeepSeek API 对请求字段的确切要求按平台文档调整。6. 常见问题与排查清单在实际使用 DeepSeek Harness 或接入 Codex 的过程中下面这些问题是出现频率最高的。问题现象常见原因解决思路401 认证失败API Key 缺失、错误或自带换行检查.env中的 key确认无引号、无空格404 模型不存在模型名写错登录平台确认当前可用模型名400 请求格式错误请求字段与接口不匹配先 curl 验证直连再排查转发层reasoning_content 报错thinking mode 下推理上下文未回传升级转发服务或关闭 thinking mode请求超时并发过高或上下文过长降低 max_workers、减少 max_tokens限流错误触发了 API 速率限制增加退避重试降低并发Codex 无法识别配置配置字段版本不兼容查看 Codex 官方文档对齐字段如果你被某个报错卡住推荐按下面的顺序排查先用curl直连 DeepSeek API确认 Key 和模型名没问题。再确认你的调用方式是直连 chat completions还是经过本地转发服务。接着看日志重点检查 assistant 消息里是否保留了reasoning_content。最后再考虑并发、超时、限流等工程层问题。下面是一个简单的 curl 验证命令curl https://api.deepseek.com/chat/completions \ -H Content-Type: application/json \ -H Authorization: Bearer $DEEPSEEK_API_KEY \ -d { model: deepseek-chat, messages: [{role: user, content: 你好}] }如果这一步能返回正常结果说明 API Key、网络和模型名都没问题问题大概率出在 Harness 或转发层。7. 最佳实践与工程建议7.1 密钥与配置管理API Key 必须放在环境变量或密钥管理服务中不要写进代码仓库。.env文件要加入.gitignore团队协作时使用.env.example来同步变量名而不是真实密钥。如果需要管理多个模型提供方的配置可以使用社区里的配置切换工具这类工具的本质是帮你维护 Codex 的配置文件但要注意及时升级到支持 thinking mode 的版本。7.2 评测资产要版本化Golden Set 是 Harness 的核心资产。建议把测试用例纳入版本管理每次增删用例都写清楚原因。模型升级后先跑全量回归再决定是否切换线上模型。7.3 成本与限流控制大模型 API 按 token 计费批量评测时要重点关注两个指标prompt_tokens 和 completion_tokens。建议在 Harness 脚本中自动统计成本并设置单次评测的预算上限。同时重试策略很重要。推荐使用指数退避算法第一次失败后等待 1 秒第二次 2 秒第三次 4 秒避免在限流边缘反复横跳。7.4 隐私与安全边界调用外部 API 时务必对请求内容做脱敏处理。涉及生产数据、用户隐私、内部代码逻辑等内容要在跨境或外部调用前进行风险评估。如果你在公司内部使用应确认数据外发是否合规必要时切换到本地部署模型通过 Ollama 或 vLLM 开启 OpenAI 兼容接口再复用同一套 Harness。7.5 从“能用”走向“可观测”Harness 开发最容易被低估的是可观测性。日志里至少要能回答三件事请求是什么时候发出的、用了哪个模型、为什么返回这个结果。建议把 trace_id 贯穿转发层和业务层这样排查问题时能快速定位链路。8. 总结与学习路线到这里你已经完成了 DeepSeek Harness 从概念到实战的一轮完整学习。你应该掌握了几个关键点Harness 和 Agent 的区别、批量评测脚本的编写方式、DeepSeek thinking mode 的reasoning_content回传原理以及 Codex 接入 DeepSeek 时的配置思路。下一步可以继续往这几个方向深入学习更专业的评估工具和评测集建设方法把精确匹配升级为 LLM Judge 打分。研究 OpenTelemetry 或 Langfuse把 Harness 日志接入可视化追踪平台。在真实项目中尝试用 Codex 这类 Agent 工具完成代码任务并记录成功率和失败模式。如果你对底层实现感兴趣可以阅读开源评估框架的源码理解它是如何调度并发请求、处理重试和保存状态的。最推荐的做法是不要停留在复制本文的脚本找一个你实际工作中经常遇到的场景收集几十条真实问题整理成评测集然后跑一次批量评估。当你看到一份结构化报告摆在面前时你对“大模型工程化开发”的理解会上一个新台阶。如果这篇文章对你有帮助可以收藏备用后续接入新的模型或迁移工具链时会经常用到。
返回列表