# litellm-opencode **Repository Path**: poenr/litellm-opencode ## Basic Information - **Project Name**: litellm-opencode - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-07-04 - **Last Updated**: 2026-07-05 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 大模型智能体能力专项测试工程 ## 〇、文档导航 本文档按"为什么测 → 测什么 → 怎么测 → 怎么用 → 怎么评"的顺序组织: | 章节 | 内容 | | ---- | ---- | | 一 | 工程背景(被测对象与接入信息) | | 二 | 工程目标 | | 三 | 六大待测能力维度 + OpenCode 会话稳定性专项的详细定义 | | 四 | LiteLLM 网关接入与请求格式 | | 五 | 工程架构设计(目录、模块、流程、日志、报告) | | 六 | 测试用例设计(JSON 外置契约)+ 用例管理指南 | | 七 | 评分细则(每维度的量化打分规则) | | 八 | 异常处理与稳定性策略 | | 九 | 输出物说明 | | 十 | 使用方式 | | 十一 | 局限性、风险与边界 | | 十二 | 后续扩展与迭代规划 | --- ## 一、工程背景 算法平台已集成 LiteLLM 大模型网关,目前网关中已部署如下 **4 个** 私有化大模型: | 序号 | 模型名称 | 模型来源 | 文件大小 | 部署方式 | 上下文长度 | 思维支持 | | ---- | ----------------------- | -------------------------------------------------------| -------- | ------------------ | ---------- | -------- | | 1 | Ornith-1.0-9B | deepreinforce-ai/Ornith-1.0-9B | 18G | sglang + 4090*2 | 262144 | ✅ | | 2 | Ornith-1.0-35B | deepreinforce-ai/Ornith-1.0-35B | 70G | sglang + 4090*2 | 262144 | ✅ | | 3 | Qwen3.6-27B | Qwen/Qwen3.6-27B-FP8 | 58G | sglang + 4090*2 | 262144 | ✅ | | 4 | Qwythos-9B | empero-ai/Qwythos-9B-Claude-Mythos-5-1M-GGUF(MTP-Q8_0) | 18G | llama.cpp + 4090*2 | 262144 | ✅ | 四个模型均私有化部署,分别使用 sglang 或 llama.cpp 推理框架运行,再经 LiteLLM 网关对外提供 OpenAI 兼容的访问接口。 --- ## 二、工程目标 针对 **OpenCode 智能体** 使用场景,编写一套对三个大模型的智能体能力进行**专项测试**的脚本,量化评估每个模型在 OpenCode Agent 工作流中的可用性,为 OpenCode 选用后端模型提供选型依据。 --- ## 三、待测能力维度 针对 OpenCode 智能体的实际工作流,本工程对每个模型按以下**六大能力维度 + 一项 OpenCode 会话稳定性专项**进行测试。前六个维度评估"能力正确性",第七个维度评估"长时间 Agent 会话下的稳定性与超时容错"。 ### 1. 思维链能力(Chain of Thought) - **测试目的**:验证模型在执行复杂任务前能否进行多步推理,体现 Agent 在拆解任务、规划步骤时的可靠性。 - **测试方法**:在请求中开启 `chat_template_kwargs.enable_thinking=true`,让模型显式输出思考过程。设计需要多步推理才能得出答案的题目(数学推理、逻辑分析、代码问题诊断)。 - **评估指标**: - 模型是否输出 `thinking`/`reasoning_content` 字段 - 最终答案正确率 - 推理步骤的逻辑连贯性(关键词 / 步骤数量检查) ### 2. 工具调用能力(Tool Calling) - **测试目的**:OpenCode 智能体核心依赖模型调用工具的能力(读文件、写文件、执行命令、搜索等)。验证模型能否正确选择工具、提取参数。 - **测试方法**:以 OpenAI tools 格式注入一组模拟工具(get_weather、calculator、search_code、read_file、write_file 等),向模型发送需要调用工具才能完成的请求。 - **评估指标**: - 工具选择正确率 - 参数提取的字段完整度与准确率 - 多工具链式调用的可行性(一次响应返回多个 tool_calls) - 非法参数 / 类型错误的处理 ### 3. 长上下文能力(Long Context) - **测试目的**:三个模型均宣称支持 262144(256K)上下文。验证模型在长上下文下的信息检索与综合能力。 - **测试方法**:根据每个模型自身配置的上下文长度独立构造多档位长文本,在文本的不同位置(前段 / 中段 / 尾段)插入"关键信息",要求模型检索或基于该信息回答。 - **评估指标**: - 不同位置信息的召回率 - 长上下文下首字延迟(TTFT)、总耗时、tokens/s - 是否出现截断、复读、答非所问等现象 ### 4. 代码生成能力(Code Generation) - **测试目的**:OpenCode 智能体主要面向代码生成场景,需评估模型在多种语言、多种复杂度的代码生成质量。 - **测试方法**: - 简单函数实现(如排序、字符串处理) - 算法题(LeetCode Easy/Medium 级别,给定题目描述与函数签名) - 多文件/模块化设计(让模型设计一个小型 Python 包结构) - Bug 修复(提供有 bug 的代码,让模型修复) - **评估指标**: - 代码可执行率 - 通过测试用例的比例 - 代码风格(命名、注释、复杂度) ### 5. 指令遵循能力(Instruction Following) - **测试目的**:验证模型对系统提示、格式约束、角色限制等复杂指令的遵从度。 - **测试方法**:设计带强约束的 prompt,例如: - 输出格式约束(JSON、Markdown 表格、特定标签) - 角色扮演约束(限定模型以某身份回答) - 否定指令(禁止使用某些词汇) - 多重约束并存(同时满足多个限制条件) - **评估指标**: - 格式输出合规率 - 角色保持度(多轮中是否偏离角色) - 否定指令遵从率 ### 6. 多轮会话能力(Multi-turn Conversation) - **测试目的**:OpenCode 智能体本质上是多轮交互,验证模型在多轮对话中的上下文保持、一致性、指代消解能力。 - **测试方法**: - 上下文指代消解("它"、"第一个"、"上面那个"等指代能否正确解析) - 信息累加(多轮逐步提供条件,模型能否综合给出最终结论) - 矛盾检测(在多轮中故意前后矛盾,看模型能否识别) - 长程记忆(20 轮以上对话后回顾早期信息) - **评估指标**: - 指代消解正确率 - 信息综合准确率 - 矛盾识别率 - 早期信息召回率 ### 7. OpenCode 会话稳定性专项(Session Stability / Timeout) - **测试目的**:OpenCode 在实际任务中会进行**长时间、多轮、频繁工具调用**的 Agent 会话,最常见的失败模式不是"答错",而是 `The operation timed out`——HTTP 客户端超时 / 网关超时 / 模型推理超时 / 流式连接中断等。本维度专门评估三个模型在 OpenCode 真实工作流下的**稳定性与超时容错能力**,为 OpenCode 工程化部署提供可靠性依据。 - **测试方法**:模拟 OpenCode 真实会话模式构造若干压力场景,每个场景运行并监控所有可能的超时源: - `sustained_multiturn`:单会话连续 50 / 100 / 200 轮对话,每轮让模型基于历史回答,检测累计耗时增长趋势 - `agent_loop`:模拟 OpenCode "读文件 → 思考 → 调工具 → 思考 → 写文件" 的典型 Agent 循环,连续执行 30 个工具调用周期 - `streaming_long_response`:请求长输出(4096+ tokens),监测流式连接中途断开 - `heavy_context_with_tools`:长上下文(64K+)下频繁触发工具调用 - `concurrent_sessions`:同模型并发 3 / 5 / 10 个会话,观察是否会因资源争抢导致超时 - `idle_then_resume`:会话保持 5 / 15 / 30 分钟空闲后继续,检测网关/后端连接是否被过早回收 - **超时来源分类**(核心评估维度): | 超时源 | 检测方式 | 影响范围 | | ------ | -------- | -------- | | 客户端 HTTP 超时 | Python `requests`/`openai` 抛 `Timeout` 异常 | 单次请求 | | LiteLLM 网关超时 | 网关返回 504 / `Gateway Timeout` | 单次请求 | | sglang / llama.cpp 推理超时 | 网关返回 503 或 504 + 后端日志 | 单次请求 | | 流式连接中途断开 | SSE 流中断,后续 chunk 缺失 | 流式会话 | | 长上下文 OOM | 网关返回 500 + 后端 CUDA OOM | 单次请求 | | 静默卡死(无错误但长时间无响应) | 自定义 watchdog,超过 P99 耗时 2 倍判定 | 单次请求 | - **评估指标**: - 各场景下**超时发生率**(出现 `The operation timed out` 等错误的请求占比) - **平均会话寿命**(出现首次超时时已完成的轮数) - **P50 / P95 / P99 响应耗时**(按场景统计) - **流式连接完整率**(流式响应无中断完成的比率) - **并发退化曲线**(并发数 ↑ → 成功率 ↓ 的关系) --- ## 四、LiteLLM 网关接入 - 网关访问地址:`http://172.28.138.204/litellm`(**末尾不带斜杠**,避免 openai SDK 拼接 `v1/chat/completions` 时产生双斜杠) - 调用方式:OpenAI 兼容的 `/v1/chat/completions` 接口。 **API Key 配置(必读)**: 为避免密钥泄露,工程统一通过**环境变量**注入 API Key: ```bash # 1. 复制 .env.example 为 .env,填入真实 Key cp .env.example .env # 2. .env 中填入: LITELLM_API_KEY=sk-your-real-key-here # 3. 运行前加载环境变量 export $(cat .env | xargs) # Linux/macOS # 或 PowerShell: # Get-Content .env | ForEach-Object { $k, $v = $_ -split '='; Set-Item -Path "Env:$k" -Value $v } ``` `config.json` 中的 `gateway.api_key` 字段固定为占位符 `${LITELLM_API_KEY}`,由 `lib/client.py` 在启动时从环境变量替换。**禁止将真实 Key 提交到仓库**,`.gitignore` 已忽略 `.env`。 调用样例(统一格式,区别仅在 `model` 字段): ```bash curl -X POST http://172.28.138.204/litellm/v1/chat/completions \ -H "Content-Type: application/json" \ -H "Authorization: Bearer ${LITELLM_API_KEY}" \ -d '{ "model": "Ornith-1.0-9B", "messages": [ {"role": "user", "content": "Hello, how are you?"} ], "chat_template_kwargs": {"enable_thinking": false} }' ``` > **关于 `enable_thinking` 的使用约束**: > - 思维链能力测试时强制设置 `enable_thinking=true` > - 其余五个能力维度统一设置 `enable_thinking=false` 以保证结果可对比 > - 这样可避免 thinking 模式污染其他维度的格式合规率、性能基线与多轮上下文空间 > - 报告头部明确标注每个维度使用的 `enable_thinking` 值,便于复现 --- ## 五、工程架构设计 ### 5.1 技术选型 - **语言**:Python 3.10+ - **依赖**:`openai` 官方 SDK(兼容 LiteLLM 网关)、`requests`(备用)、`json`/`re`/`logging`(内置) - **报告输出**:Markdown 报告 + 每模型独立日志文件 - **运行方式**:CLI 命令行一键运行全部测试 ### 5.2 目录结构 ``` litellm-opencode/ ├── README.md # 本文档(功能设计说明) ├── config.json # 网关配置、模型清单、测试参数 ├── requirements.txt # Python 依赖 ├── .env.example # 环境变量示例(API Key 占位) ├── .gitignore # 忽略 .env / __pycache__ / logs/ / results/ ├── run_tests.py # 主入口:一键执行所有能力测试 ├── lib/ │ ├── __init__.py │ ├── client.py # LiteLLM 网关客户端封装(注入 tiktoken、解析 ${ENV}) │ ├── evaluator.py # 通用评估器(断言/打分) │ └── reporter.py # Markdown 报告生成器(带 O(1) 索引去重) ├── tests/ │ ├── __init__.py │ ├── test_chain_of_thought.py # 思维链能力测试 │ ├── test_tool_calling.py # 工具调用能力测试 │ ├── test_long_context.py # 长上下文能力测试 │ ├── test_code_generation.py # 代码生成能力测试 │ ├── test_instruction_following.py # 指令遵循能力测试 │ ├── test_multiturn.py # 多轮会话能力测试 │ └── test_stability.py # OpenCode 会话稳定性/超时专项测试 ├── scripts/ │ └── bg-run.ps1 # 后台执行 + 进度监控的 PowerShell 包装器 ├── prompts/ # 测试用 prompt 模板与测试集 │ ├── cot_cases.json │ ├── tool_cases.json │ ├── long_context_cases.json │ ├── code_cases.json │ ├── instruction_cases.json │ ├── multiturn_cases.json │ ├── stability_cases.json │ └── stability_cases.README.md # 稳定性用例集的设计说明与重构记录 ├── fixtures/ # 长上下文噪声源样本 │ └── wikipedia_zh_sample.txt # 中英文混合噪声文本(用于构造 target_tokens 长度的输入) ├── logs/ # 每个模型独立日志文件(文件名带执行开始时间戳) │ ├── Ornith-1.0-9B_20260702_153045.log │ ├── Qwen3.6-27B_20260702_153045.log │ ├── Qwythos-9B_20260702_153045.log │ └── .bg/ # bg-run.ps1 后台任务专用目录 │ ├── pids/.pid # 任务 PID 文件 │ └── logs/.log # 后台任务输出(UTF-8) └── results/ # Markdown 测试报告 └── report_YYYYMMDD_HHMMSS.md ``` > **当前工程状态(2026-07-03)**:`lib/`、`tests/`、`run_tests.py`、`requirements.txt`、`scripts/bg-run.ps1` 已全部实现并通过 Qwen3.6-27B 实测验证(69 case,PASS 81.2%)。`prompts/stability_cases.README.md` 记录了稳定性用例集从 14 个精简到 6 个的重构过程。 ### 5.3 核心模块职责 #### `config.json` - LiteLLM 网关 `base_url` / `api_key`(占位符 `${LITELLM_API_KEY}`,运行时由 `lib/client.py` 从环境变量替换) / `timeout` / `max_retries` - `test_params.default`:所有能力维度未显式配置时的**兜底默认参数**(`temperature` / `max_tokens` / `enable_thinking`),`lib/client.py` 在 `resolve_params` 中按 `default → ability_specific → model_override` 顺序合并 - 各能力维度专属参数(覆盖 default):`chain_of_thought` / `tool_calling` / `long_context` / `code_generation` / `instruction_following` / `multiturn` / `stability` - 长上下文**共用基础档** `long_context.base_tiers`(所有模型必跑,每个档位覆盖 head/middle/tail 三个位置) - 长上下文**噪声源** `long_context.noise_source`(默认 `fixtures/wikipedia_zh_sample.txt`)与** token 估算方式** `long_context.token_estimator`(支持 `tiktoken:` 格式,如 `tiktoken:cl100k_base`;`lib/client.py` 根据此字段动态加载 tiktoken 编码器) - 长上下文**模型专属档** `models[].extra_tiers`(每个模型按自身硬件承载能力拓展) - 模型清单 `models[]`(含 `max_context_length` / `supports_thinking` / `extra_tiers`) - `model_limits`:模型部署时的硬限制(如 `max_concurrent_sessions`),由 `tests/test_stability.py::_run_concurrent` 自动 clamp 超限的 case 参数并 warning - `model_param_overrides`:模型在特定维度的参数覆盖(详见下文) > `config.json` 的完整结构示例参见工程根目录下的 [`config.json`](./config.json) 文件。 **关键设计点**: - **基础档抽出**:`long_context.base_tiers` 是所有模型共用的"基准线",四个模型必须跑这三个档以保证横向可比 - **模型只列差异**:每个模型的 `extra_tiers` 仅记录其专属的极限档,避免重复字段淹没关键差异 - **测试参数扁平化**:每个能力维度的 `enable_thinking` 直接平铺在该维度下,与第四章"enable_thinking 使用约束"一致 - **新增模型成本极低**:复制一个 `models[]` 元素,写好 `name` / `max_context_length` / `extra_tiers`,并在 `model_limits` 添加对应限制即可 - **档位压力比**:报告中展示 `target_tokens / max_context_length` 比值,用于评估模型在自身极限附近的衰减 - **模型硬限制 (`model_limits`)**:4 个模型的 `max_concurrent_sessions` 限制: - Qwen3.6-27B:**2**(硬限制:3 并发 p99 86s 超 60s 阈值,5 并发 100% Connection error) - 其他 3 个模型:3(默认限制) **关于 `model_param_overrides`(模型参数覆盖)**: 部分模型可能在特定能力维度上需要偏离 `test_params` 的默认行为(如强制开启思考模式、调整 temperature 等)。配置方式: - **顶层独立字段** `model_param_overrides`:与 `models[]` 解耦,模型元数据与行为覆盖分离 - **结构**:key 为模型名,value 为 `{<维度名>: {<字段>: <新值>}}`,只写需要覆盖的字段 - **合并规则**(在 `lib/client.py` 实现 `resolve_params(ability, model_name)`): ```python base = test_params[ability] # 默认 override = model_param_overrides.get(model_name, {}).get(ability, {}) # 覆盖 return {**base, **override} # 后写覆盖前写 ``` - **未配置覆盖的模型/维度**:完全继承 `test_params`,无副作用 - **示例含义**:Ornith-1.0-9B 在 `tool_calling` 和 `code_generation` 两个维度强制开启思考模式(`enable_thinking=true`),并相应调整 `temperature` 与 `max_tokens`;其余维度继承默认 - **报告追溯**:报告元信息中展示每个模型每个维度**实际生效**的 `enable_thinking` 值(维度 × 模型矩阵),便于复现 新增模型特殊行为只需在 `model_param_overrides` 增加一个键值,无需改动 `test_params` 与 `models[]`。 #### `lib/client.py` - 基于 `openai.OpenAI` 封装统一的 `LLMClient` - 启动时从 `os.environ` 解析 `${LITELLM_API_KEY}` 占位符并注入 `config.json.gateway.api_key` - 根据 `long_context.token_estimator` 动态加载 tiktoken 编码器(如 `tiktoken:cl100k_base`),用于长上下文用例的 target_tokens 控制 - 提供 `chat(model, messages, tools=None, **kwargs)` 方法(支持 `stream=True` 流式) - 自动注入 `chat_template_kwargs.enable_thinking` - 返回统一的 `ChatResponse` 数据结构(content / reasoning_content / tool_calls / usage / latency / finish_reason) #### `lib/evaluator.py` - 通用评估函数: - **字符串包含**:`expected_answer_contains` / `forbidden_words` / `forbidden_regex` - **正则匹配**:`expected_answer_regex` / `value_checks.` 中的正则 - **多模式正则**:`value_checks.` 支持**字符串数组**,evaluator 任一命中即通过(用于容忍多种合法表达,如 tool_002 的表达式匹配) - **JSON 解析与键校验**:`expected_format=json` 时解析为 dict,校验 `expected_keys` / `expected_key_aliases`(支持别名,如 `fruits` ↔ `fruit` ↔ `水果`) - **JSON 数组数量约束**:`expected_array_count: {key: n}` 校验指定键对应的数组长度恰好为 n - **代码可执行性检查**:从模型输出提取代码片段,按 `function_signature` 注入 `test_cases` 并执行,比对返回值 - **类方法调用**:`kwargs.instance_method` 字段触发 `ClassName().method(*args)` 调用约定 - **自定义操作**:`kwargs.op` 字段触发 evaluator 内部注册的 op(如 `add_then_list_count` / `complete_returns_bool` / `delete_missing_returns_bool`) - **角色保持度**:`expected_role_keywords` + `role_consistency_min_hits` 校验多轮中角色身份关键词累计出现次数 - **长度约束**:`max_word_count` / `max_sentence_count`(按空格或句号切分计数) - **话题切换容忍**:`expected_answers` 中填 `N/A_topic_switch` 跳过该轮评分(用于 topic_switch_then_back 场景的干扰轮) - **矛盾识别**:`must_not_contain` 字段校验指定轮不应包含的关键词(与 `expected_thresholds` 配合判定 contradiction 场景) - 每个能力维度对应的评分规则(详见 §七) #### `lib/reporter.py` - 汇总所有模型所有维度的测试结果 - 生成 Markdown 表格(按模型 × 能力维度) - 写入 `results/report_*.md` ### 5.4 测试执行流程 ``` run_tests.py │ ├─ 加载 config.json ├─ 初始化 LLMClient ├─ 为每个模型初始化独立 Logger(logs/__.log,时间戳为本次执行开始时间) │ ├─ for 模型 in models: │ for 能力 in [chain_of_thought, tool_calling, long_context, │ code_generation, instruction_following, multiturn, │ stability]: │ ├─ 取 params = resolve_params(能力, 模型名) # 合并 test_params + model_param_overrides │ ├─ 长上下文维度:合并 base_tiers + 当前模型 extra_tiers │ ├─ 调用 tests/test_<能力>.py 中的 run(client, logger, model, params) │ └─ 记录 TestResult 到内存列表 │ └─ 调用 reporter 生成 Markdown 报告 -> results/report_.md ``` ### 5.5 日志策略 - 每个模型独立日志文件 `logs/__.log` - **命名规则**:`` 为模型名(如 `Ornith-1.0-9B`),`_` 为**本次测试执行开始时间**(精确到秒,24h 制) - **同一执行内共享**:一次 `python run_tests.py` 调用产生的所有模型日志使用**同一时间戳**,便于按执行批次归档与对比 - **跨次执行隔离**:每次新执行会生成新的时间戳文件,旧日志不会被覆盖 - **示例**:`logs/Ornith-1.0-9B_20260702_153045.log`、`logs/Qwen3.6-27B_20260702_153045.log`、`logs/Qwythos-9B_20260702_153045.log` - 日志级别 INFO,按时间顺序记录: - 测试开始/结束时间 - 每条用例的 `session_id`(多轮用例内一致)/ `request_id`(每次请求唯一)/ `turn_index`(多轮内轮次索引) - 每条用例的 prompt(截断展示) - 模型原始响应(截断展示) - 评分结果(PASS/FAIL + 得分) - 异常堆栈(如有) - `concurrent_sessions` 场景下,并发的 N 个会话通过 `session_id` 区分(形如 `<用例id>_s<并发序号>`),避免日志交错混淆 - 关键事件同时输出到控制台,便于实时观察进度 ### 5.6 报告策略 - 单次执行生成一份 Markdown 报告,文件名带时间戳 - 报告结构: - 测试元信息(开始/结束时间、模型列表、测试维度、`enable_thinking` 开关矩阵) - 综合评分表(模型 × 能力维度 矩阵) - 各能力维度详情(每个模型在该维度的得分、典型用例、失败用例) - 长上下文独立成节(按模型差异化展示 + 同档位横向对比) - 结论与建议(综合排名、OpenCode 选型建议) --- ## 六、测试用例设计(JSON 外置契约) ### 6.0 总体设计原则 **所有测试用例均以外部 JSON 文件形式独立管理,与代码及本文档完全解耦**: - 每个能力维度对应一个 JSON 文件,位于 `prompts/<能力>_cases.json` - 本章只定义**字段契约(schema)**与**覆盖场景清单**,**不预设任何具体 prompt 文案** - 新增、删除、调整用例仅需修改对应 JSON 文件,无需改动代码或本文档 - 用例文件按"基础示例集 + 自定义扩展"组织,基础集用于横向可比,自定义扩展用于专项探测 **JSON 文件格式约定(统一规范)**: | 字段 | 必填 | 说明 | | ---- | ---- | ---- | | `id` | 是 | 用例唯一 ID,建议 `<维度前缀>_<3 位序号>`,如 `cot_001`、`tool_002` | | `tags` | 否 | 标签数组,便于按场景筛选(`math` / `easy` / `format_json` 等) | | `enabled` | 否 | 布尔值,默认 true;置 false 可临时跳过该用例(无需删除) | | `weight` | 否 | 浮点数,默认 1.0;用于加权评分 | | 业务字段 | 是 | 各维度自定义(见下文 schema) | **加载与筛选机制**: - `run_tests.py` 启动时加载所有 `prompts/*.json` - 长上下文用例额外加载 `config.json.long_context.noise_source`(默认 `fixtures/wikipedia_zh_sample.txt`)作为噪声文本池 - 默认只跑 `enabled != false` 的用例 - CLI 参数 `--only-tags math` 可按 tag 筛选子集 - CLI 参数 `--exclude-tags bug_fix` 可排除指定 tag - CLI 参数 `--quick` 等价于每个文件仅取前 2 条 ### 6.1 思维链用例(`cot_cases.json`) **字段契约**: ```json { "id": "cot_001", "tags": ["math", "medium"], "prompt": "<具体题目文案,由用例维护者编写>", "expected_keywords": ["推理过程中应出现的关键词"], "expected_answer": "<最终答案>", "score_rule": { "answer_match": 1.0, "keyword_min_hits": 3, "thinking_required": true } } ``` **覆盖场景清单**(建议每个场景 ≥ 3 条用例): - 数学推理(多步算术 / 比例问题 / 行程问题) - 逻辑推理(命题逻辑 / 条件推理) - 代码问题诊断(读代码找 bug / 解释代码行为) - 因果分析(多因素归因) ### 6.2 工具调用用例(`tool_cases.json`) **字段契约**: ```json { "id": "tool_001", "tags": ["single_tool"], "user_prompt": "<用户问题文案>", "system_prompt": "<可选系统提示>", "tools": [ {"type": "function", "function": {"name": "<工具名>", "parameters": {}}} ], "accept_multi_turn": false, "expected_calls": [ { "name": "<期望调用的工具名>", "required_args": ["<必填参数名数组>"], "optional_args": ["<可选参数名数组>"], "value_checks": { "<参数名>": "<期望值、正则字符串或正则数组(任一命中即通过)>" } } ], "no_call_expected": false, "expected_content_keywords": [""] } ``` **字段说明**: - `accept_multi_turn`:布尔值,true 表示允许模型分多轮响应再完成全部工具调用(适用 chain_tool / multi_tool 中存在依赖关系的场景);false 表示要求在一次响应中完成所有 `expected_calls` - `value_checks.`:支持**单字符串**(正则)或**字符串数组**(任一正则命中即通过)。用于容忍工具参数的多种合法表达(如 `(123+456)*7-89` 与 `123+456*7-89` 等价) - `expected_content_keywords`:仅在 `no_call_expected=true` 时生效,校验模型回复文本中是否包含预期关键词(如解释 Python 装饰器时应包含"装饰器"/"wrapper"等) - `value_checks.` 除字符串/数组外,对于"具体值"型参数(如 capacity=10),可直接写数值(evaluator 做精确等值比对) **覆盖场景清单**: - `single_tool`:单工具调用 - `multi_tool`:一次响应中调用多个工具(无依赖关系) - `no_tool`:用户问题不需要工具,验证模型不应乱调工具(`no_call_expected: true`,且 prompt 设计应让"调用工具"与"不调用工具"难以区分以提高测试区分度) - `chain_tool`:工具 A 输出作为工具 B 输入(`accept_multi_turn: true`) - `param_extraction`:从自然语言中抽取结构化参数 - `nested_schema`:参数结构嵌套(数组 / 对象) ### 6.3 长上下文用例(`long_context_cases.json` + `config.json`) **重要设计原则**:长上下文**基础档**所有模型共享用于横向对比;**专属档**根据每个模型自身配置的上下文长度独立构造。原因: - 不同模型推理框架(sglang / llama.cpp)实际可承载上下文可能与宣称值有偏差 - 不同显存/算力条件下同一档位性能差异显著,统一档位会掩盖模型真实能力 - 共享基础档 + 模型专属档的组合,既保证可比性又体现差异化 - 独立配置可灵活适配后续新增模型,无需改测试代码 **位置覆盖要求**:每个档位必须覆盖 **head / middle / tail** 三个位置(`positions: ["head", "middle", "tail"]`),用于发现"位置遗忘"现象——key_fact 放在文档前段、中段、尾段的召回率差异 **字段契约**(与档位配置解耦): ```json { "id": "lc_001", "tags": ["needle", "fact_recall"], "key_fact": "<注入到长文中的关键事实>", "needle_question": "<针对关键事实的提问>", "expected_answer_contains": ["<期望答案中必含的关键词>"], "expected_answer_regex": ["<可选正则约束>"], "key_positions": ["head", "middle", "tail"] } ``` **档位构成(基础档 + 模型专属档)** 每个模型最终跑的全部档位 = `long_context.base_tiers` + `models[].extra_tiers`。 **共用基础档(`long_context.base_tiers`,所有模型必跑)**: | 档位名 | target_tokens | 占典型 max_ctx | 位置覆盖 | 用例数 | | ------- | ------------- | -------------- | --------------------- | ------ | | short | 4096 | 1.5% | head + middle + tail | 3 | | medium | 16384 | 6.2% | head + middle + tail | 3 | | long | 65536 | 25% | head + middle + tail | 3 | **模型专属档(`models[].extra_tiers`)**: | 模型 | 部署 | 显存 | 专属档 | | ------------------------ | ---------- | ----- | ------------------------------------------------------------------------------------- | | Ornith-1.0-9B | sglang | 18G | ultra (128K) + max_safe (200K) | | Qwen3.6-27B | sglang | 58G | ultra (128K) + extreme (200K) + limit (240K) | | Qwythos-9B | llama.cpp | 18G | ultra (128K) | > **设计说明**: > - 每个模型跑 `base_tiers (3档) + extra_tiers` 的并集 > - llama.cpp 部署的 Qwythos 因显存与推理特性,保守不跑 200K+ 档,避免 OOM > - 58G 的 Qwen3.6 显存最充裕,可跑至 240K 极限档 > - 每个位置(head/middle/tail)单独计分,便于发现"位置遗忘"现象 > - 报告中展示 `target_tokens / max_context_length` 比值(档位压力),便于横向对比同压力下的能力差异 **运行时用例构造方式**: 1. 准备"噪声文本"池(如 wikipedia 段落、Lorem ipsum、代码片段) 2. 按 `target_tokens` 循环拼接噪声至目标长度 3. 在 `key_positions` 指定位置插入 `key_fact` 4. 拼接 `needle_question` 作为用户请求 `long_context.noise_source` 指定噪声文本来源,`token_estimator` 指定 token 估算方式(按字符/4 估算或调用 tokenizer)。 ### 6.4 代码生成用例(`code_cases.json`) **字段契约**: ```json { "id": "code_001", "tags": ["simple_function", "python", "easy"], "language": "python", "prompt": "<具体任务描述>", "function_signature": "<函数签名或类签名(用于从模型输出提取并执行测试)>", "test_cases": [ { "input": [<位置参数>], "kwargs": {<关键字参数,支持 instance_method / op 等约定字段>}, "expected": <期望返回值,支持具体数值或特定字符串(如 'N/A_topic_switch' / 'id_present')>, "description": "<用例说明>" } ], "score_rule": { "exec_required": true, "edge_cases": ["empty", "unicode", "large_input"] } } ``` **kwargs 约定字段**: - `instance_method`:当 `function_signature` 是 `class Xxx` 时,evaluator 期望测试调用 `Xxx().instance_method(*args, **kwargs)`,从 kwargs 读取方法名 - `op`:自定义操作标识,evaluator 内部注册对应的 op handler(如 `add_then_list_count` / `complete_returns_bool` / `delete_missing_returns_bool`),用于复杂类的多步骤行为测试 - 其他 kwargs 透传给被调函数 **评分规则扩展**: - `score_rule.exec_required`:true 表示必须能成功执行;false 表示仅检查代码语法与可读性 - `score_rule.edge_cases`:用例覆盖的边界场景标签,用于报告中标注"模型在该 edge case 上失败" **覆盖场景清单**: - `simple_function`:简单函数实现 - `algorithm`:算法题(排序、二分、动态规划等,按 Easy/Medium/Hard 标注) - `bug_fix`:给定有 bug 的代码让模型修复(bug 必须是运行时错误而非语法错误,确保代码能运行) - `refactor`:代码重构 - `design`:模块/类设计题 **多语言覆盖**:当前已覆盖 `python` / `javascript` / `go` / `java` / `typescript`,新增语言仅需追加用例并指定 `language` 字段。 ### 6.5 指令遵循用例(`instruction_cases.json`) **字段契约**: ```json { "id": "ins_001", "tags": ["format_json"], "instruction_type": "format_json", "system_prompt": "<约束系统提示>", "user_prompt": "<用户任务>", "expected_format": "json", "expected_keys": ["<期望输出 JSON 的必含 key>"], "expected_key_aliases": {"": ["<该 key 的可接受别名>"]}, "expected_array_count": {"": }, "forbidden_words": ["<禁止出现的词>"], "forbidden_regex": ["<禁止出现的正则模式(如 emoji 表情)>"], "language_constraint": "<可选:强制输出语言,如 'en' / 'zh'>", "role_constraint": "<可选:限定角色身份>", "expected_role_keywords": ["<角色保持度评分用的关键词>"], "role_consistency_min_hits": <最少命中次数>, "max_word_count": <单词数上限>, "max_sentence_count": <句子数上限> } ``` **字段说明**: - `expected_key_aliases`:当 `expected_keys` 中的 key 不可用时,可接受的别名列表(用于容忍单复数差异、中英文差异等,如 `fruits` ↔ `fruit` ↔ `水果`) - `expected_array_count`:校验 JSON 中指定 key 对应数组的长度恰好为 N(用于"推荐恰好 N 项"类约束) - `forbidden_regex`:正则数组,匹配任一即视为违规(用于禁止 emoji 表情等) - `expected_role_keywords` + `role_consistency_min_hits`:角色保持度评分,校验模型响应中是否包含足够的角色身份关键词(最少命中 `min_hits` 个) - `max_word_count`:英文按空格切分计数,中文按字符切分计数(自动检测) - `max_sentence_count`:按句号/问号/感叹号切分计数 **覆盖场景清单**: - `format_json` / `format_markdown_table` / `format_xml` - `role_play`:限定角色身份(配合 `expected_role_keywords` 做保持度评分) - `negative`:禁止使用某些词(`forbidden_words` 非空) - `multi_constraint`:同时满足 ≥3 个约束 - `language`:限定输出语言 - `length_constraint`:长度/句子数约束 ### 6.6 多轮会话用例(`multiturn_cases.json`) **字段契约**: ```json { "id": "mt_001", "tags": ["coreference"], "scenario": "coreference", "system_prompt": "<可选系统提示>", "turns": [ {"role": "user", "content": "<第 1 轮用户输入>"} ], "expected_answers": [ null, "<第 N 轮期望答案(与 turns 一一对应;陈述/无期望轮填 null)>", "N/A_topic_switch" ], "evaluation": { "answer_match_each_turn": true, "consistency_check": false, "evaluated_turns": [2, 3, 5], "topic_keywords": {"2": ["关键词"], "5": ["关键词"]}, "must_not_contain": {"5": ["应排除的关键词"]} } } ``` **字段说明**: - `expected_answers`:与 `turns` 一一对应。**null** 表示该轮是陈述/无答案(不参与评分);**`N/A_topic_switch`** 表示该轮是话题切换干扰轮,仅校验模型确实回应了新话题(通过 `topic_keywords`),不要求精确匹配 - `evaluation.evaluated_turns`:显式声明参与评分的轮次索引(1-based),避免与 `turns` 数组长度不一致时的歧义 - `evaluation.topic_keywords`:键为轮次索引,值为关键词数组;evaluator 校验该轮响应是否包含全部关键词(任一缺失即扣分) - `evaluation.must_not_contain`:键为轮次索引,值为关键词数组;evaluator 校验该轮响应不应包含任一关键词(用于 contradiction 场景:纠正后不应再提被纠正的内容) - `expected_answers` 数量必须与 `turns` 一致,长度校验在加载阶段完成(不满足直接 ERROR) **覆盖场景清单**: - `coreference`:指代消解 - `incremental`:信息累加 - `contradiction`:前后矛盾检测(用 `must_not_contain` 校验纠正后的最终答案) - `long_memory`:20+ 轮后回顾早期信息(手写真实填充,禁止用占位符) - `topic_switch_then_back`:话题切换再回到原话题(用 `N/A_topic_switch` 标记干扰轮) ### 6.7 OpenCode 会话稳定性用例(`stability_cases.json`) **字段契约**(结构与前 6 个维度不同——更侧重"运行配置"与"预期阈值"): ```json { "id": "stab_001", "tags": ["sustained_multiturn"], "scenario": "sustained_multiturn", "rounds": 100, "agent_steps": 30, "system_prompt": "", "pre_session_turns": [ {"role": "user", "content": ""}, {"role": "assistant", "content": "<前置会话的助手回复>"} ], "user_turn_template": "<每轮用户输入模板,可引用历史摘要占位符>", "tools": [<可选工具列表,仅 agent_loop 场景使用>], "concurrent_n": 1, "idle_minutes": 0, "stream_required": false, "expected_thresholds": { "max_timeout_rate": 0.01, "max_p99_latency_ms": 30000, "min_completion_rate": 0.95 }, "watchdog_ms": 60000, "step_semantics": "" } ``` #### 用例字段说明 | 字段 | 含义 | | ---- | ---- | | `scenario` | 场景类型,对应下方 6 种之一 | | `rounds` | 多轮场景的总轮数(sustained_multiturn / multiturn-style 场景使用) | | `agent_steps` | Agent 循环场景的总工具调用次数(agent_loop 场景使用,与 rounds 互斥) | | `concurrent_n` | 并发会话数(concurrent_sessions 场景使用) | | `idle_minutes` | 空闲等待时长(idle_then_resume 场景使用) | | `pre_session_turns` | 空闲恢复场景的前置会话内容(idle_then_resume 必填),用于先建立上下文再空闲 | | `stream_required` | true 表示本用例必须使用流式调用(streaming_long_response 场景置 true) | | `system_prompt` | 注入的 OpenCode 风格系统提示 | | `user_turn_template` | 每轮用户输入模板(可引用历史摘要) | | `tools` | Agent 循环场景下注入的模拟工具集 | | `expected_thresholds` | 通过阈值:超时率上限 / P99 延迟上限 / 完成率下限(统一用 `min_completion_rate`,不再区分流式/非流式) | | `watchdog_ms` | 单次请求最长等待时间,超出即判定为"静默卡死" | | `step_semantics` | agent_loop 场景的语义说明(人类可读,用于报告展示) | #### 场景清单与默认参数 | scenario | 默认参数 | 验证目标 | | -------- | -------- | -------- | | `sustained_multiturn` | rounds = 50, 100, 200 | 长会话累积耗时增长 | | `agent_loop` | agent_steps = 30,工具 5 个 | 工具循环稳定性(步骤计数) | | `streaming_long_response` | rounds = 1, max_tokens = 4096 / 8192, stream_required=true | 流式连接完整性 | | `heavy_context_with_tools` | rounds = 10, ctx = 64K / 128K | 长上下文 + 工具混合 | | `concurrent_sessions` | rounds = 30, n = 3, 5, 10 | 并发退化曲线 | | `idle_then_resume` | rounds = 4, idle_min = 5, 15, 30 | 连接保活能力(前置会话保留上下文) | #### 超时检测实现 ```python try: resp = client.chat(model=model, messages=messages, tools=tools, timeout=watchdog_ms / 1000) return SuccessResult(resp) except openai.APITimeoutError as e: return TimeoutResult(source="client_http", error=str(e)) except openai.APIStatusError as e: if e.status_code in (504, 503): return TimeoutResult(source="gateway_or_backend", error=str(e)) return ErrorResult(source="api_status", error=str(e)) except Exception as e: if "operation timed out" in str(e).lower(): return TimeoutResult(source="unknown", error=str(e)) return ErrorResult(source="other", error=str(e)) ``` 每个 `TimeoutResult` 必须标注 `source`,便于报告中按超时源分类统计。 ### 6.8 测试用例管理 由于用例完全外置为 JSON 文件,调整用例**无需修改代码或本文档**。常见操作如下: #### 新增用例 1. 打开对应的 `prompts/<能力>_cases.json` 2. 按该维度的字段契约追加一条 JSON 对象 3. 必须填 `id`(全局唯一),其他字段按契约补齐 4. 保存即可,下次运行 `python run_tests.py` 自动加载 #### 临时禁用用例 ```json { "id": "tool_005", "enabled": false, ... } ``` 无需删除,置 `enabled: false` 即可跳过。 #### 按场景筛选子集 ```bash # 仅跑标签为 math 的思维链用例 python run_tests.py --only-tags math # 排除 bug_fix 类用例(用于日常回归) python run_tests.py --exclude-tags bug_fix # 调试模式:每个文件仅取前 2 条 python run_tests.py --quick ``` #### 调整评分权重 在 `score_rule` 或顶层 `weight` 中调整数值,无需改 `lib/evaluator.py`。 #### 用例版本管理建议 - JSON 文件纳入 Git 版本控制 - 每次新增/删除/大幅修改用例建议在 commit message 中注明 - 关键改动(影响评分结论的)应同步更新"已知用例集版本"到报告中 #### 用例文件总览 | 文件 | 用途 | | --------------------------------- | --------------------------------- | | `prompts/cot_cases.json` | 思维链能力测试用例 | | `prompts/tool_cases.json` | 工具调用能力测试用例 | | `prompts/long_context_cases.json` | 长上下文关键事实与提问模板 | | `prompts/code_cases.json` | 代码生成测试用例 | | `prompts/instruction_cases.json` | 指令遵循测试用例 | | `prompts/multiturn_cases.json` | 多轮会话测试用例 | | `prompts/stability_cases.json` | OpenCode 会话稳定性压力场景配置 | > **禁止**:不要把实际测试 prompt 文案硬编码到 `lib/` 或 `tests/` 模块里。所有文案必须可追溯到对应 JSON 文件。 --- ## 七、评分细则 ### 7.1 各维度评分公式 | 维度 | 评分公式 | 满分构成 | | ------------ | ------------------------------------------------------------------------ | ------------------------------------------- | | 思维链 | `0.6 × 答案正确率 + 0.2 × 思考输出完整度 + 0.2 × 推理关键词命中率` | 答案 60% + 思考过程 20% + 关键词 20% | | 工具调用 | `0.5 × 工具选择正确率 + 0.3 × 参数完整度 + 0.2 × 无幻觉调用率` | 选择 50% + 参数 30% + 不乱调用 20% | | 长上下文 | `0.7 × 关键信息召回率 + 0.2 × 任务完成率 + 0.1 × 性能稳定性` | 召回 70% + 任务 20% + 性能 10% | | 代码生成 | `0.7 × 用例通过率 + 0.2 × 代码可执行率 + 0.1 × 代码风格分` | 通过 70% + 可执行 20% + 风格 10% | | 指令遵循 | `0.5 × 格式合规率 + 0.3 × 内容正确率 + 0.2 × 否定指令遵从率` | 格式 50% + 内容 30% + 否定 20% | | 多轮会话 | `0.4 × 指代消解 + 0.3 × 信息综合 + 0.2 × 一致性 + 0.1 × 矛盾识别` | 指代 40% + 综合 30% + 一致 20% + 矛盾 10% | | 会话稳定性 | `0.5 × (1 − 超时率) + 0.3 × 会话完成率 + 0.2 × P99 延迟合规率` | 不超时 50% + 完成 30% + 性能 20% | ### 7.2 综合得分(用于模型横向对比) 每个模型**七个维度等权平均**得到综合分(0~100): ``` total_score = mean([cot, tool, long_ctx, code, instruction, multiturn, stability]) ``` > **OpenCode 选型场景下的特殊权重**:若用户主要关注 OpenCode 工程化部署,可将 `stability` 权重提至 2×,因其反映的是"能否稳定运行"而非"能否答对"。 报告输出**综合分排名表**与**雷达图(用 Markdown 表格近似表示)**。 ### 7.3 性能基线(长上下文维度附属) 仅在长上下文维度记录性能数据: - TTFT(首 token 延迟,ms) - 总耗时(s) - tokens/s(吞吐) - 显存占用(nvidia-smi 采样,可选) 不直接计入总分,但在报告中作为"工程可落地性"参考项列出。 --- ## 八、异常处理与稳定性策略 ### 8.1 网络与网关异常 - 单次请求设置 `timeout=120s`(长上下文档位 600s) - 失败自动重试:`max_retries=2`,指数退避(1s / 3s) - 重试仍失败:标记该用例 `ERROR`,记入日志,不中断整个测试流程 - 网关整体不可达:立即终止测试,输出错误报告 ### 8.2 模型输出异常 - 空响应 / 截断响应:标记 `EMPTY` / `TRUNCATED`,不计分但保留原始输出到日志 - 工具调用格式错误(解析不到 `tool_calls`):记 `MALFORMED` - 代码生成不可执行:`exec_pass_rate=0`,但保留代码便于人工复盘 - 思维链未输出(即便 `enable_thinking=true`):标记 `NO_THINKING`,该维度单独说明 ### 8.3 资源限制 - 长上下文 200K+ 用例单独执行,避免抢占小用例资源 - 每个模型串行执行(避免单机多模型抢占) - 日志按模型独立落盘,单模型失败不影响其他模型 - **`concurrent_sessions` 场景实测经验**(4 模型对比): - **Qwen3.6-27B**(硬限制 ≤2 并发): - 1-2 并发:所有请求可正常完成,p99 latency 50-60s - 3 并发:p99 latency ~85s(**超 60s 阈值 FAIL**) - 5 并发:100% Connection error(LiteLLM 网关连接被拒绝) - **强制限制**:`config.json::model_limits.Qwen3.6-27B.max_concurrent_sessions=2`,由 `test_stability.py::_run_concurrent` 自动 clamp - **Ornith-1.0-9B / Ornith-1.0-35B / Qwythos-9B**(默认 ≤3 并发): - 3 并发:100% 完成,p99 latency < 25s - 更高级别并发未测试 - **建议**:在 OpenCode 部署中显式限制 `max_concurrent_sessions` 参照 `config.json::model_limits` 配置 - **stability watchdog 优化**:`tests/test_stability.py::_run_concurrent` 显式传 `timeout=watchdog_ms/1000` 给 `client.chat()`,单请求严格按 watchdog 超时(默认 60s),避免长尾请求拖累整体进度 - **multiturn watchdog 保护**:`tests/test_multiturn.py` 新增 `turn_watchdog_ms`(默认 90s)和 `case_watchdog_ms`(默认 300s),防止 LiteLLM 网关 hang 时阻塞整个测试 - **长上下文硬件限制**:vLLM 后端实际可用上下文通常 < 宣称值(如 Qwen3.6-27B 宣称 256K,实测 vLLM 限制 87K tokens),200K+ 档用例必然失败,需要在 `models[].extra_tiers` 中标注 `max_safe` 边界 ### 8.4 可复现性 - `config.json` 固定 `temperature`(思维链 0.6,其余 0.2)和 `seed`(如网关支持) - **seed 回退策略**:LiteLLM 网关默认不向下游透传 `seed` 字段。若某次运行报告 `SEED_NOT_SUPPORTED`,evaluator 自动启用以下回退: 1. 将 `temperature` 强制设为 0(仅对支持 temperature=0 的模型) 2. 若模型不支持 temperature=0,则记录"本次结果不可严格复现"并继续运行 3. 报告元信息中标注 `reproducibility: strict/relaxed/none` 三档 - 报告头部记录 `config.json` 的 SHA256 hash,便于追溯配置版本 ### 8.5 `enable_thinking` 兼容性兜底 - 若模型不支持 `enable_thinking=true`(响应中无 `reasoning_content` 字段),思维链维度自动降级为"评估最终答案正确率 + 在 prompt 中显式要求 step-by-step" - 降级行为在报告中标注 `THINKING_FALLBACK` - 其余维度若发现响应异常包含大量 think 块,需检查 `enable_thinking` 是否被错误开启 - **思维块污染检测**:evaluator 在评估最终答案前,统计响应中 `...` 块长度;若占比 > 50% 但该维度 `enable_thinking=false`,标记 `THINKING_BLEED` 并在报告中提示 - **fallback 触发后行为**:`models[].supports_thinking=false` 的模型在所有维度统一使用 `enable_thinking=false`,不再尝试开启 --- ## 九、输出物说明 | 输出 | 路径 | 用途 | | ------------------- | ------------------------------- | --------------------------------- | | Markdown 报告 | `results/report_<时间戳>.md` | 整体测试结果汇总,供评审与归档 | | 模型独立日志 | `logs/<模型名>__.log` | 查看每次执行的每个模型的完整测试过程与异常 | | 后台任务日志 | `logs/.bg/logs/.log` | `bg-run.ps1 start` 后台任务的实时输出(UTF-8 编码) | | 后台任务 PID | `logs/.bg/pids/.pid` | `bg-run.ps1` 跟踪后台进程的文件,可手动清理 | | 控制台实时输出 | 终端 | 跟踪测试进度 | ### 9.1 Markdown 报告结构 ```markdown # 大模型智能体能力测试报告 - 测试时间:2026-07-02 10:00:00 ~ 11:30:00 - 网关地址:http://172.28.138.204/litellm - 测试维度:7 项(六大能力 + OpenCode 会话稳定性) - 涉及模型:3 个 - enable_thinking 矩阵(按模型 × 维度,标注实际生效值): | 维度 | Ornith-1.0-9B | Qwen3.6-27B | Qwythos-9B | | ------------ | ----------------- | ----------- | ---------- | | 思维链 | true | true | true | | 工具调用 | true (override) | false | false | | 长上下文 | false | false | false | | 代码生成 | true (override) | false | false | | 指令遵循 | false | false | false | | 多轮会话 | false | false | false | | 会话稳定性 | false | false | false | > 上表 Ornith-1.0-9B 的 `tool_calling` 与 `code_generation` 实际生效参数来自 `model_param_overrides`,分别为: > - tool_calling:`{enable_thinking: true, temperature: 0.4, max_tokens: 2048}` > - code_generation:`{enable_thinking: true, temperature: 0.4, max_tokens: 3072}` ## 1. 综合评分表 | 模型 | 思维链 | 工具调用 | 长上下文 | 代码生成 | 指令遵循 | 多轮会话 | 会话稳定性 | 总分 | 排名 | | ---- | ------ | -------- | -------- | -------- | -------- | -------- | ---------- | ---- | ---- | ## 2. 各能力维度详情 ### 2.1 思维链 - Ornith-1.0-9B:得分 xx - 典型通过用例:... - 典型失败用例:... ... ### 2.7 OpenCode 会话稳定性 - Ornith-1.0-9B:综合得分 xx - 各场景超时率:sustained_multiturn xx% / agent_loop xx% / streaming xx% / heavy_context xx% / concurrent xx% / idle_then_resume xx% - 超时源分布:client_http x 次 / gateway x 次 / backend x 次 / streaming_disconnect x 次 / oom x 次 / silent_freeze x 次 - P99 延迟:xxx ms - OpenCode 部署建议:... ## 3. 长上下文详情(基础档 + 模型专属档) ### 3.1 各模型档位明细 #### 3.1.1 Ornith-1.0-9B(跑了 3 基础档 + 2 专属档 = 5 档) | 档位 | target_tokens | 档位压力 | 位置 | 召回率 | TTFT(ms) | 总耗时(s) | tokens/s | | ---- | ------------- | -------- | ---- | ------ | -------- | --------- | -------- | #### 3.1.2 Qwen3.6-27B(跑了 3 基础档 + 3 专属档 = 6 档) ... #### 3.1.3 Qwythos-9B(跑了 3 基础档 + 1 专属档 = 4 档) ... ### 3.2 同档位横向对比(仅基础档,所有模型都有数据) | 模型 | short 召回 | medium 召回 | long 召回 | short tokens/s | medium tokens/s | long tokens/s | | ---- | ---------- | ----------- | --------- | -------------- | --------------- | ------------- | ## 4. 结论与选型建议 - 综合排名:... - OpenCode 智能体场景建议:... ``` --- ## 十、使用方式 ### 10.1 前台执行(标准用法) ```bash # 安装依赖 pip install -r requirements.txt # 配置 API Key(一次性) cp .env.example .env # 编辑 .env,填入 LITELLM_API_KEY=sk-... # 执行全部模型 × 全部能力的测试 python run_tests.py # 仅测试指定模型 python run_tests.py --models Ornith-1.0-9B Qwen3.6-27B # 仅测试指定能力维度 python run_tests.py --abilities tool_calling code_generation # 调试模式:每个维度只跑前 2 个用例 python run_tests.py --quick # 按 tag 过滤用例 python run_tests.py --only-tags math reasoning python run_tests.py --exclude-tags bug_fix # 指定输出报告文件名 python run_tests.py --report my_report.md # dry-run:仅验证 config 和用例 JSON,不调用网关 python run_tests.py --dry-run # 多模型并行(每个模型独立 LLMClient,节省总时间) python run_tests.py --parallel-models # 稳定性 case 子集(默认 standard=6 case) python run_tests.py --abilities stability # 6 case,~5min/模型 python run_tests.py --abilities stability --stability-preset minimal # 3 case,~4min/模型 python run_tests.py --abilities stability --stability-preset full # 6 case(同 standard,留扩展位) # 4 模型对比(建议用 --parallel-models 节省时间) python run_tests.py # 跑所有 4 模型完整 74 case python run_tests.py --models Ornith-1.0-9B Ornith-1.0-35B # 仅跑 2 个 Ornith ``` ### 10.2 4 模型对比分析(典型用法) ```bash # 跑稳定性对比(~16 min,4 模型 × 6 case) python run_tests.py --abilities stability --report report_stab_4models.md # 跑思维链对比(~9 min,4 模型 × 10 case) python run_tests.py --abilities chain_of_thought --report report_cot_4models.md # 跑全量对比(建议后台 + 并行,~2-4 小时) .\scripts\bg-run.ps1 start "python run_tests.py --parallel-models --report report_full_4models.md" -Name full4 ``` ### 10.2 断点续跑(崩溃后恢复) ```bash # 场景:跑了 60/74 个 case 后中断(崩溃、用户中止等),想从 multiturn 开始继续 # --start-ability:从指定 ability 开始,跳过之前的 ability python run_tests.py --models Qwen3.6-27B \ --start-ability multiturn \ --resume-log logs/Qwen3.6-27B_20260703_073449.log \ --report report_qwen_resume.md # --resume-log:可多次传入,按时间顺序合并历史 RESULT python run_tests.py --models Qwen3.6-27B \ --resume-log logs/Qwen3.6-27B_20260703_073449.log \ --resume-log logs/Qwen3.6-27B_20260703_092601.log \ --abilities stability # --skip-case:注入 SKIP 状态(如某 case 已知不可执行) python run_tests.py --models Qwen3.6-27B \ --skip-case "Qwen3.6-27B:stab_010:5并发30轮被网关拒绝" \ --resume-log logs/Qwen3.6-27B_073449.log # resume-log 解析的 RESULT 会自动覆盖 reporter 中已存在的同 case_id # 这样多次重跑会得到正确的"最后一次"结果 ``` ### 10.3 后台执行(长时间测试推荐) 完整 74 case 测试约需 50-60 分钟,建议用 `scripts/bg-run.ps1` 后台执行并实时监控: ```powershell # 启动后台任务(自动注入 LITELLM_API_KEY、UTF-8 编码) .\scripts\bg-run.ps1 start "python run_tests.py --models Qwen3.6-27B" -Name qwen_full # 实时查看日志尾部(Ctrl+C 停止 tail 不影响后台任务) .\scripts\bg-run.ps1 tail -Name qwen_full 50 # 查看任务状态(PID、CPU、运行时间、最后一行日志) .\scripts\bg-run.ps1 status -Name qwen_full # 等待任务完成(可设置超时秒数) .\scripts\bg-run.ps1 wait -Name qwen_full 3600 # 列出所有后台任务 .\scripts\bg-run.ps1 list # 停止后台任务 .\scripts\bg-run.ps1 stop -Name qwen_full # 清理已退出但残留 PID 文件的 stale 任务 .\scripts\bg-run.ps1 clean ``` 后台任务的所有输出(UTF-8 编码)保存在 `logs/.bg/logs/.log`,与控制台实时同步。 --- ## 十一、局限性、风险与边界 - **Qwen3.6-27B 硬限制 max_concurrent_sessions ≤ 2**:3 并发 p99 latency 85s(超 60s 阈值 FAIL),5 并发 100% Connection error。由 `config.json::model_limits` 强制,自动 clamp 超限的 `concurrent_n` 并 warning。建议在 OpenCode 部署中: - 显式配置 `max_concurrent_sessions=2` 或加入请求队列 - 进一步定位需检查 LiteLLM 网关的 `RATE_LIMIT` / 后端 vllm/sglang 的 `max_num_seqs` 配置 - **其他 3 个模型 max_concurrent_sessions ≤ 3**:3 并发测试通过,更高级别并发未测试。如需 5+ 并发需先做扩展验证。 - **稳定性用例精简**:当前 `prompts/stability_cases.json` 保留 6 个 case(已重构),原始 14 个 case 中的 `sustained_multiturn 100/200rounds` / `concurrent_sessions n10` / `idle_then_resume 5/15/30min` 等被移除。详细原因参见 `prompts/stability_cases.README.md` - **`fixtures/wikipedia_zh_sample.txt` 待补充**:长上下文用例依赖该噪声源文件,首次运行前需补齐(建议中英文混合、≥ 100KB 样本量) - **私有模型评测偏差**:本测试基于 LiteLLM 网关,无法直接控制底层推理框架的采样参数,可能引入不可控随机性 - **评分主观性**:代码风格、推理连贯性、角色保持度等评分存在一定主观成分,必要时引入多评估者对比 - **测试集规模有限**:每个维度用例数为 6~18 条(多轮用例最长 14 轮),结论仅作参考,不能替代大规模评测 - **长上下文硬件瓶颈**:256K 上下文对显存/算力要求高,本环境(2×4090)下 vLLM 实际只支持到 87K tokens(实测 `Input length (97841 tokens) exceeds the maximum allowed length (87927 tokens)`),200K 档用例全部失败 - **API Key 安全**:API Key 通过环境变量注入(参见 §四),**禁止将 `.env` 提交到仓库** - **OpenCode 实际工作流差异**:本测试为离线能力测试,与 OpenCode 真实运行环境可能存在 prompt / 工具集差异,最终选型建议结合端到端实测 ## 十一.1 4 模型实测对比摘要 ### 稳定性对比(standard preset, ~16 min/批) | 模型 | 通过率 | 速度 | 并发能力 | 推荐 | |---|---|---|---|---| | Qwythos-9B | 6/6 | 🥇 1.3 min | p99 < 2s | 实时场景 | | Ornith-1.0-9B | 6/6 | 🥈 2.9 min | p99 < 25s | 通用 | | Ornith-1.0-35B | 6/6 | 🥉 4.6 min | streaming 最优 | 长输出 | | Qwen3.6-27B | 5/6 | 9.3 min | **仅 2 并发** | 受限使用 | ### 思维链对比(10 case × 4 模型,~9 min/批) | 模型 | 通过率 | 平均分 | 优势 | |---|---|---|---| | **Ornith-1.0-35B** | 8/10 (80%) | **0.84** 🥇 | 因果推理 (cot_008/009) 唯一全 PASS | | Qwen3.6-27B | 8/10 (80%) | 0.82 🥈 | 慢但稳 | | Qwythos-9B | 5/10 (50%) | 0.76 | 9B 中略好 | | Ornith-1.0-9B | 5/10 (50%) | 0.74 | cot_005 复杂逻辑 0.65 | ### 关键发现 - 4 模型共同弱点:**cot_006 (Python bug) 和 cot_007 (SQL 复杂查询)** —— 所有模型都只能拿 0.46-0.58 分 - 35B 模型综合最佳(Ornith-1.0-35B)但耗时是 9B 模型的 2 倍 - 复杂代码诊断是当前 4 模型的共同瓶颈 > 完整对比报告:`results/report_stab_4models.md` 和 `results/report_cot_4models.md` --- ## 十二、后续扩展与迭代规划 ### 短期 - **恢复 stability_cases.json 早期 case**:根据 `prompts/stability_cases.README.md` 记录,补充 `sustained_multiturn 100/200rounds` / `idle_then_resume 5/15/30min` 等 - **优化 `lib/client.py` 的 max_retries**:stability 场景下连接错误应快速失败而非重试 - **补充 `fixtures/wikipedia_zh_sample.txt`**:提供 ≥ 100KB 中英文混合噪声源样本 - 调整 vLLM 部署使 200K 上下文可用(当前 87K 上限) - 增加更多模型(接入新模型仅需在 `config.json` 的 `models[]` 添加一项) - 扩充各维度测试用例集(目标每维度 ≥ 30 用例) - 增加更多能力维度(如多语言、安全性、抗诱导) ### 中期 - 接入 CI,定时回归测试,对比不同版本模型的能力变化 - 报告自动生成对比趋势图(结合历史报告 diff) - 对接 OpenCode 实际工作流,跑真实任务进行端到端评估 - 定位并修复 LiteLLM 网关在 5 并发场景下的连接被拒绝问题 ### 长期 - 接入评测平台(如 OpenCompass、lm-evaluation-harness) - 引入人工评分机制,覆盖主观维度 - 形成完整的模型选型 → 上线监控 → 反馈迭代闭环