Skip to content

winterless/AgenticRLDataPreparing

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

40 Commits
 
 
 
 
 
 
 
 

Repository files navigation

快速开始(推荐)

  • 同步测试脚本(以 README 为真源)
python scripts/tests/generate_test_scripts.py
  • 跑全量(不依赖 LLM prompt)
./scripts/tests/full_generate_test.sh
  • 跑单文件 Demo(不依赖 LLM prompt)
./scripts/tests/single_generate_test.sh
  • 覆盖默认变量(任意脚本前加环境变量即可):
    • 示例:WORKERS=64 RAW_ROOT=/path/to/data ./scripts/tests/full_generate_test.sh

流程概览(原始轨迹 ➜ 混淆 ➜ MCQ ➜ 组装)

  • 生成/准备轨迹:parquet → jsonl(generate_toucan.py
  • 统计与 alias:抽取函数签名/元数据、生成 alias(function_stats.py --alias-output
  • 混淆:用 alias 重写工具/函数名(obfuscate_jsonl.py
  • 基于混淆数据重建统计 + 参数池function_stats.py + build_param_pool.py
  • 生成 MCQ(脚本模式:build_has_api_script.py;全量批处理:batch_generate.py
  • 组装对话+MCQassemble_toucan.py

data/demo/ 内是最小可跑样例:所有 “single” 默认输出到 ${DEMO_DIR}


命令清单(README 为真源,自动生成 scripts/tests/*_generate_test.sh

下面这段 bash 代码块会被 scripts/tests/generate_test_scripts.py 解析:

  • # vars:会写入生成的 .sh 顶部(默认值可被环境变量覆盖)
  • # full / # single:各自进入对应脚本
  • # online:会被跳过(只做文档示例)
  • # test:只做说明,不进入脚本
展开:用于自动生成测试脚本的命令(# vars / # full / # single)
# vars
WORKERS="${WORKERS:-32}"

# full pipeline roots
RAW_ROOT="${RAW_ROOT:-Toucan-1.5M/Toucan-1.5M}"
OBF_ROOT="${OBF_ROOT:-data/Toucan-1.5M-obf}"
GENERATE_ROOT="${GENERATE_ROOT:-data/Toucan-1.5M-generate}"

# stats outputs
STATS_DIR="${STATS_DIR:-stats}"
STATS_RAW_CSV="${STATS_RAW_CSV:-${STATS_DIR}/function_stats_raw.csv}"
STATS_RAW_JSON="${STATS_RAW_JSON:-${STATS_DIR}/function_stats_raw.json}"
ALIAS_JSON="${ALIAS_JSON:-${STATS_DIR}/function_alias.json}"
STATS_CSV="${STATS_CSV:-${STATS_DIR}/function_stats.csv}"
STATS_JSON="${STATS_JSON:-${STATS_DIR}/function_stats.json}"
PARAM_POOL="${PARAM_POOL:-${STATS_DIR}/param_pool.json}"

# stage2 / ablation knobs (assemble_toucan.py)
TASK_SELECT_TAG="${TASK_SELECT_TAG:-<TASK=SELECT>}"
ANSWER_REDACT="${ANSWER_REDACT:-none}" # none|redact|drop
MCQ_SUBSAMPLE="${MCQ_SUBSAMPLE:-1}"  # 0..1 (default: 90% records keep MCQs)
MCQ_SUBSAMPLE_SEED="${MCQ_SUBSAMPLE_SEED:-0}"
MCQ_TAG="${MCQ_TAG:-}"                 # e.g. "[MCQ]" (empty disables)
EMIT_NO_MCQ_TAG="${EMIT_NO_MCQ_TAG:-0}" # 0|1 (1 emits [NO_MCQ] marker on no-MCQ records)
RANDOM_ALIAS_PER_RECORD="${RANDOM_ALIAS_PER_RECORD:-1}" # 0|1
RANDOM_ALIAS_SEED="${RANDOM_ALIAS_SEED:-0}"
EMIT_LOSS_MASK_TAGS="${EMIT_LOSS_MASK_TAGS:-0}" # 0|1 (wrap context blocks with loss_mask=0 tags)
LOSS_MASK0_BEGIN_TAG="${LOSS_MASK0_BEGIN_TAG:-<LOSS_MASK=0>}"
LOSS_MASK0_END_TAG="${LOSS_MASK0_END_TAG:-</LOSS_MASK=0>}"
SPLIT_SHARDS="${SPLIT_SHARDS:-0}"      # 0|1
SPLIT_SHARDS_FLAG=""
if [ "${SPLIT_SHARDS}" = "1" ]; then
  SPLIT_SHARDS_FLAG="--split-shards"
fi
EMIT_METADATA="${EMIT_METADATA:-0}"    # 0|1 (1=emit)
EMIT_METADATA_FLAG=""
if [ "${EMIT_METADATA}" = "1" ]; then
  EMIT_METADATA_FLAG="--emit-metadata"
fi
EMIT_QUESTION_QUALITY="${EMIT_QUESTION_QUALITY:-0}"  # 0|1 (1=emit)
EMIT_QUESTION_QUALITY_FLAG=""
if [ "${EMIT_QUESTION_QUALITY}" = "1" ]; then
  EMIT_QUESTION_QUALITY_FLAG="--emit-question-quality"
fi
EMIT_RESPONSE_QUALITY="${EMIT_RESPONSE_QUALITY:-0}"  # 0|1 (1=emit)
EMIT_RESPONSE_QUALITY_FLAG=""
if [ "${EMIT_RESPONSE_QUALITY}" = "1" ]; then
  EMIT_RESPONSE_QUALITY_FLAG="--emit-response-quality"
fi
EMIT_NO_MCQ_TAG_FLAG=""
if [ "${EMIT_NO_MCQ_TAG}" = "1" ]; then
  EMIT_NO_MCQ_TAG_FLAG="--emit-no-mcq-tag"
fi
RANDOM_ALIAS_PER_RECORD_FLAG=""
if [ "${RANDOM_ALIAS_PER_RECORD}" = "1" ]; then
  RANDOM_ALIAS_PER_RECORD_FLAG="--random-alias-per-record"
fi
EMIT_LOSS_MASK_TAGS_FLAG=""
if [ "${EMIT_LOSS_MASK_TAGS}" = "1" ]; then
  EMIT_LOSS_MASK_TAGS_FLAG="--emit-loss-mask-tags"
fi

# demo (single) paths
DEMO_DIR="${DEMO_DIR:-data/demo}"
DEMO_PARQUET="${DEMO_PARQUET:-Toucan-1.5M/Toucan-1.5M/Kimi-K2/train-00000-of-00040.parquet}"
DEMO_RAW_JSONL="${DEMO_RAW_JSONL:-${DEMO_DIR}/toucan_raw.jsonl}"
DEMO_PRETTY_TXT="${DEMO_PRETTY_TXT:-${DEMO_DIR}/toucan.txt}"
DEMO_JSONL="${DEMO_JSONL:-${DEMO_DIR}/toucan.jsonl}"
DEMO_API_AVAILABLE="${DEMO_API_AVAILABLE:-${DEMO_DIR}/toucan_api_available.jsonl}"
DEMO_API_PARAMS="${DEMO_API_PARAMS:-${DEMO_DIR}/toucan_api_params.jsonl}"
DEMO_API_PARAM_VALUES="${DEMO_API_PARAM_VALUES:-${DEMO_DIR}/toucan_api_param_values.jsonl}"

# ---------- full: end-to-end (no prompt) ----------
# full
python scripts/data_preprocess/generate_toucan.py \
  -i "${RAW_ROOT}" --drop-non-utf8 --workers "${WORKERS}"

# full
python scripts/analysis/function_stats.py \
  -i "${RAW_ROOT}" \
  -o "${STATS_RAW_CSV}" \
  --meta-output "${STATS_RAW_JSON}" \
  --alias-output "${ALIAS_JSON}" \
  --workers "${WORKERS}"

# full
python scripts/data_preprocess/obfuscate_jsonl.py \
  -i "${RAW_ROOT}" \
  -o "${OBF_ROOT}" \
  --alias "${ALIAS_JSON}" \
  --alias-scope global \
  --workers "${WORKERS}"

# full
python scripts/analysis/function_stats.py \
  -i "${OBF_ROOT}" \
  -o "${STATS_CSV}" \
  --meta-output "${STATS_JSON}" \
  --workers "${WORKERS}"

# full
python scripts/data_preprocess/build_param_pool.py \
  -i "${OBF_ROOT}" \
  -s "${STATS_JSON}" \
  -o "${PARAM_POOL}" \
  --workers "${WORKERS}"

# full
python scripts/build_has/batch_generate.py \
  -i "${OBF_ROOT}" \
  -o "${GENERATE_ROOT}" \
  -s "${STATS_JSON}" \
  --workers "${WORKERS}" \
  --param-pool "${PARAM_POOL}"

# full
python scripts/data_postprocess/assemble_toucan.py \
  -i "${OBF_ROOT}" \
  -m "${GENERATE_ROOT}" \
  --workers "${WORKERS}" \
  --task-select-tag "${TASK_SELECT_TAG}" \
  --answer-redact "${ANSWER_REDACT}" \
  --mcq-tag "${MCQ_TAG}" \
  ${EMIT_NO_MCQ_TAG_FLAG} \
  ${RANDOM_ALIAS_PER_RECORD_FLAG} \
  --random-alias-seed "${RANDOM_ALIAS_SEED}" \
  ${EMIT_LOSS_MASK_TAGS_FLAG} \
  --loss-mask0-begin-tag "${LOSS_MASK0_BEGIN_TAG}" \
  --loss-mask0-end-tag "${LOSS_MASK0_END_TAG}" \
  --mcq-subsample "${MCQ_SUBSAMPLE}" \
  --mcq-subsample-seed "${MCQ_SUBSAMPLE_SEED}" \
  ${SPLIT_SHARDS_FLAG} \
  ${EMIT_METADATA_FLAG} \
  ${EMIT_QUESTION_QUALITY_FLAG} \
  ${EMIT_RESPONSE_QUALITY_FLAG}

# ---------- single: demo (no prompt) ----------
# single
python scripts/data_preprocess/generate_toucan.py \
  -i "${DEMO_PARQUET}" \
  --sample-size 1 \
  --seed 23 \
  --drop-non-utf8 \
  -o "${DEMO_RAW_JSONL}"

# single
python scripts/analysis/pretty_toucan.py \
  -i "${DEMO_RAW_JSONL}" \
  -n 1 > "${DEMO_PRETTY_TXT}"

# single
python scripts/data_preprocess/obfuscate_jsonl.py \
  -i "${DEMO_RAW_JSONL}" \
  -o "${DEMO_JSONL}" \
  --alias "${ALIAS_JSON}" \
  --alias-scope global

# single
python scripts/build_has/build_has_api_script.py \
  -i "${DEMO_JSONL}" \
  -s "${STATS_JSON}" \
  -o "${DEMO_API_AVAILABLE}" \
  --mode available \
  --negatives 12

# single
python scripts/build_has/build_has_api_script.py \
  -i "${DEMO_JSONL}" \
  -s "${STATS_JSON}" \
  -o "${DEMO_API_PARAMS}" \
  --mode params \
  --negatives 5

# single
python scripts/build_has/build_has_api_script.py \
  -i "${DEMO_JSONL}" \
  -s "${STATS_JSON}" \
  -o "${DEMO_API_PARAM_VALUES}" \
  --mode param_values \
  --negatives 5 \
  --param-pool "${PARAM_POOL}"

# single
python scripts/data_postprocess/assemble_toucan.py \
  -i "${DEMO_DIR}" \
  -m "${DEMO_DIR}" \
  --task-select-tag "${TASK_SELECT_TAG}" \
  --answer-redact "${ANSWER_REDACT}" \
  --mcq-tag "${MCQ_TAG}" \
  ${EMIT_NO_MCQ_TAG_FLAG} \
  ${RANDOM_ALIAS_PER_RECORD_FLAG} \
  --random-alias-seed "${RANDOM_ALIAS_SEED}" \
  ${EMIT_LOSS_MASK_TAGS_FLAG} \
  --loss-mask0-begin-tag "${LOSS_MASK0_BEGIN_TAG}" \
  --loss-mask0-end-tag "${LOSS_MASK0_END_TAG}" \
  --mcq-subsample "${MCQ_SUBSAMPLE}" \
  --mcq-subsample-seed "${MCQ_SUBSAMPLE_SEED}" \
  ${SPLIT_SHARDS_FLAG} \
  ${EMIT_METADATA_FLAG} \
  ${EMIT_QUESTION_QUALITY_FLAG} \
  ${EMIT_RESPONSE_QUALITY_FLAG}

# ---------- optional / docs-only ----------
# test
python scripts/data_postprocess/assemble_toucan.py \
  -i "${OBF_ROOT}" \
  -m "${GENERATE_ROOT}" \
  --workers "${WORKERS}" \
  --passthrough-only

# online
python scripts/build_has/batch_generate.py \
  -i "${OBF_ROOT}" \
  -o data/has_prompt_batch \
  -s "${STATS_JSON}" \
  --prompt-mode \
  --prompt-limit 10 \
  --prompt-temperature 0.4 \
  --prompt-max-tokens 512

# online
python scripts/build_has/build_has_api_prompt.py \
  -i "${DEMO_JSONL}" \
  -s "${STATS_JSON}" \
  -o data/demo/has_prompt_toucan.jsonl \
  --temperature 0.4 \
  --max-tokens 512

关键脚本与常用参数(速查)

  • scripts/data_preprocess/generate_toucan.py:parquet → jsonl

    • -i/--input:parquet 文件或目录
    • -o/--output:输出 jsonl(不指定时按脚本默认路径)
    • --workers:并行度
    • --sample-size / --seed:抽样(demo 用)
    • --drop-non-utf8:过滤不可编码内容
  • scripts/analysis/function_stats.py:统计函数/工具元数据,可生成 alias

    • -i:输入 jsonl 文件或目录
    • -o:输出 csv
    • --meta-output:输出 json(schema/meta)
    • --alias-output:输出 alias map(从 raw 构建时用)
    • --workers:并行度
  • scripts/data_preprocess/obfuscate_jsonl.py:按 alias 混淆 jsonl

    • -i / -o:输入/输出(文件或目录)
    • --alias:alias map 路径
    • --alias-scope {global,record}:alias 作用域(默认 globalrecord 会对每条 record 重新随机一套 alias)
    • --emit-alias-map <dir>:当 --alias-scope record 时可选开启:输出逐行对齐的 alias 日志(供后续构池做反映射聚合)
    • --workers:并行度(目录模式)
  • scripts/data_preprocess/build_param_pool.py:构建参数值池(供 param_values 负样本)

    • -i:混淆后的 jsonl(文件或目录)
    • -sfunction_stats.json
    • -oparam_pool.json
    • --alias-log-dir <dir>:可选:指向 obfuscate_jsonl.py --emit-alias-map <dir> 产出的日志目录;会把 func_xxx -> 原始函数名 映射回 canonical 后再聚合,避免 key 爆炸
    • --workers:并行度
  • scripts/build_has/build_has_api_script.py:生成单文件 MCQ(demo)

    • --modeavailable | params | param_values
    • --negatives:负例数量
    • --param-poolparam_values 模式需要
  • scripts/build_has/batch_generate.py:全量批处理生成 MCQ(无 prompt 或 prompt)

    • --workers:并行度(脚本模式)
    • --param-pool:参数池
    • --prompt-mode:启用 prompt 生成(串行更适合小规模)
    • --prompt-limit / --prompt-temperature / --prompt-max-tokens:prompt 控制
  • scripts/data_postprocess/assemble_toucan.py:将轨迹 + MCQ 拼装为训练格式

    • -i/--conv-root:对话根目录
    • -m/--mcq-root:MCQ 根目录
    • --workers:并行度
    • --answer-redact {none,redact,drop}:控制是否输出 Answer: 行(默认 drop;训练版建议 redactdrop
    • --mcq-subsample <p>:按 uuid 确定性下采样 MCQ 注入比例(0..1,默认 1
    • --mcq-subsample-seed <n>:下采样随机种子(用于可复现)
    • --mcq-tag "<tag>":每个 MCQ block 前插入一行 tag(样式隔离/检索;空字符串等价于不输出)
    • --emit-no-mcq-tag:对无 MCQ 的 record 插入 [NO_MCQ] marker(用于 shard 标记/下游过滤)
    • --random-alias-per-record / --random-alias-seed <n>:记录级随机 alias,覆盖 Available tools / MCQ / function_call.name(默认开启;如需关闭用 --no-random-alias-per-record
    • --no-random-alias-per-record:关闭记录级随机 alias(默认开启)
    • --emit-alias-map-dir <dir>:输出“记录级随机 alias”的对齐日志(默认:<mcq_root>/alias_logs/assemble/;文件名:<conversation_filename>.alias_map.jsonl,逐行记录 {uuid, line_index, alias_map{original->alias}}),便于从 assembled 中反查原名
    • --no-emit-alias-map:关闭上述 alias 日志输出(默认开启;仅在 --random-alias-per-record 时生效)
    • --emit-loss-mask-tags:对“只读/上下文块”包一层 <LOSS_MASK=0> ... </LOSS_MASK=0>(便于下游做 token loss mask)
    • --loss-mask0-begin-tag / --loss-mask0-end-tag:自定义 loss_mask=0 标签
    • --split-shards:分 shard 输出 *_mcq_assembled.**_no_mcq_assembled.*
    • --emit-metadata / --emit-question-quality / --emit-response-quality:如需保留 Metadata: / Question/Response quality assessment: 段落(默认不输出)
    • --passthrough-only:不注入 MCQ,仅做 UTF-8 严格写出(消融/对比)
    • --no-text-output:只输出 jsonl,不写 txt
    • --show-function-name:MCQ 题头展示函数名(默认隐藏)

HAS-API 题型策略(脚本模式细节)

生成逻辑集中在 scripts/build_has/build_has_api_script.py

  1. available 模式

    • 仅在 assistant 且包含 function_call 的消息上出题。
    • 先使用 available_tools 中的真实候选,再补充同 family / 语义相近 alias 作为干扰项。
    • 输出格式为 alias_name,不含描述,防止模型通过原名记忆。
  2. params 模式

    • 根据 function_stats.json 中的 JSON Schema 解析必填字段。
    • 正确选项是所有 required 参数集合;干扰项包括缺失必填、加入额外 optional、或单字段组合。
  3. param_values 模式

    • 真值来自实际 function_call.arguments(字符串先 json.loads)。
    • 负例来源:
      • ParamPool(函数/参数/类型三级聚类)抽取真实历史值。
      • _mutate_with_pool 支持一次扰动 1~2 个字段。
      • _drop_argument 针对 required / 任意字段生成缺失参数的负例。
    • ParamPool.sample() 优先从相同函数/参数的历史值采样,并以较小概率注入跨函数干扰项。
参数值生成思路
1. 解析真实参数:correct_option = _format_arg_values(_parse_arguments(fc))
2. 扰动策略(按类型推断):
   - bool:取反
   - int/float:±1/2/5
   - enum string:挑其他枚举值;否则附加少量后缀
   - 其余:若无法生成则跳过
3. ParamPool:按 (函数, 参数) → 单参数 → 类型 三层聚类,并记录去重值
4. 组装题目:variations[:num_neg] + correct_option,随机打乱

以上策略保证:

  • 负样本具备语义相关性与多样性,避免模型靠“公共特征”偷懒。
  • 所有 jsonl 输出仅包含 alias 名称;pretty_toucan.py --alias-map 可在人工检查时恢复原名。

数据拼装(轨迹 + MCQ)

scripts/data_postprocess/assemble_toucan.py 会扫描 -i/--conv-root 目录下的对话 jsonl,并在 -m/--mcq-root 里查找同前缀的 _api_{available,params,param_values}.jsonl。一旦发现完整组合,就会写出 <prefix>_mcq_assembled.jsonl + <prefix>_mcq_assembled.txt(路径位于 MCQ 目录)。data/demo/ 已内置 toucan

Answer: 行是否保留由 --answer-redact {none,redact,drop} 控制(默认 drop:不输出答案;redact:输出但隐藏答案;none:输出原始答案,便于人工校验)。如不需要文本副本可使用 --no-text-output

运行方式:

  • 推荐:直接运行 README 顶部的 ./scripts/tests/full_generate_test.sh / ./scripts/tests/single_generate_test.sh
  • 消融/过滤(不注入 MCQ):运行 assemble_toucan.py --passthrough-only(见上方“命令清单”的 optional 区块)
展开:拼装规则(详细)
  1. 开头写 Question:工具清单:,需要注入这个轨迹对应MCP文件中的假工具,工具乱序。
  2. System tool declare: 来自原始 messages 中 system/tool_declare,需要注入这个轨迹对应MCP文件中的假工具,工具乱序。
  3. 展开 user/assistant/function 消息;在 assistant 准备 function_call 时,插入与该 message_index 对应的 MCQ(顺序:available → params → param_values)。 注意是每个assistant的function_call按照function,params,parma_values的顺序插入mcq
  4. MCQ 题头格式:
[MCQ:param_values|function=func_xxx|msg=4]
问:……
选项:A.… B.… C.… D.…
  1. MCQ 区块后接 function_call 参数与 function 响应,再附 [[原文回答]]
  2. 轨迹结束后补 Target tools:Question quality assessment:Response quality assessment:Metadata:

该流程确保模型既能看到完整对话,又能学习多项选择题,不暴露答案。更多细节、字段含义及 concat 规划请参考 scripts/ARCHITECTURE.md

P.S 清洗数据脚本

# test
python scripts/data_preprocess/clean_utf8_dir.py -i /path/to/src_dir -o /path/to/dst_dir --workers 32

测试

  • 测试脚本生成(自动同步 README 标签)

    # generate tests based on README.md python commands
    # test
    python scripts/tests/generate_test_scripts.py
    
  • 全量数据构建(不依赖 LLM prompt)

    ./scripts/tests/full_generate_test.sh
    
  • 单文件 Demo 构建(不依赖 LLM prompt)

    ./scripts/tests/single_generate_test.sh
    

Stage2:抑制“背函数名 / 见 MCQ 就答”的副作用(训练与数据改造清单)

现象(问题定义)

  • 用大规模 *_mcq_assembled.txt 做 CPT 后,模型在没有工具上下文、也没有被要求评估/做题时,会主动输出 MCQ 答案/评分段落
  • 行为上更像在“记住稳定的 function/alias name”,而不是“读当前可用工具描述再决定”。

根因假设(高概率)

  • 名字信号过强且稳定:同一工具在全语料里 alias 固定,极易被记忆。
  • 格式触发过强:训练文本里 MCQ/Answer/评分段落出现频繁,模型学到“看到类似上下文就输出答案/评估”。
  • 选项缺少语义对齐:MCQ options 里出现的干扰项如果不在 Available tools 中,模型只能“背名字/记分布”,无法做“基于描述选择”。

数据侧改造(优先做)

  • 记录级随机 alias:同一条记录内自洽,不同记录重新随机(避免稳定可背的名字)。
    • 已实现(jsonl 侧):scripts/data_preprocess/obfuscate_jsonl.py --alias-scope record
    • 已实现(组装输出侧):assemble_toucan.py --random-alias-per-record
    • 注意:如果你用 obfuscate_jsonl.py --alias-scope record 生成了“每条 record 一套随机 func_xxx”的混淆数据,那么构建 param_pool.json 时需要同时:
      • 混淆时开启:--emit-alias-map <alias_logs_dir>
      • 构池时传入:build_param_pool.py --alias-log-dir <alias_logs_dir> 否则会出现 param_pool.json 里几百万个 func_xxx key 的爆炸现象。
  • 弱化/隐藏名字(避免泄露原始函数名):已实现:obfuscate_jsonl.py 会重写 available_tools / messages.function_call.name / tool_calls / <tools>...</tools> 以及 MCQ 字段(question/answer/options)中的名称为 alias。
  • MCQ 干扰项描述补全(让模型能“读描述选”):已实现:assemble_toucan.py 会将 MCQ options 中出现但不在 available_tools 的函数,从 stats/function_stats.json 补齐 description + schema 并合并进同一个 Available tools 列表(不创建 “Extended tools” 区块)。
    • 默认行为:若未显式传 --stats,会自动尝试加载 stats/function_stats.json
    • 仅补当前记录涉及的少量工具(通常就是 MCQ options 里的那几个),长度可控;若仍担心上下文,可在训练里用 negative 强化“不能靠名字偷懒/必须看描述”
  • Answer 遮蔽:已实现:assemble_toucan.py --answer-redact {none,redact,drop}(默认 drop;redact 保留行但隐藏答案;none 输出原始答案用于人工校验)。
  • MCQ 下采样 + 分 shard:已实现:assemble_toucan.py --mcq-subsample <p> --split-shards(按 uuid 确定性下采样;输出 *_mcq_assembled.**_no_mcq_assembled.* 两个 shard)。
  • 加入反例:未提供工具/未要求评估时,目标输出是澄清/拒答;加入相似描述但不同 alias 的对比样本,强化“读描述”。

训练侧建议(配合数据)

  • 分阶段:先 CPT(含上面数据改造 + 下采样/隔离),再少量 SFT 强化“仅在用户要求时才做题/评估”的指令遵守。
  • 推理侧约束:system prompt 明确要求“仅基于当前可用工具描述;描述不足就澄清;未要求时禁止自出题/自打分”。

工程开关(assemble_toucan.py 已支持)

  • --stats <path>:为 MCQ options 中的干扰项补齐工具 description/schema(不传则默认尝试 stats/function_stats.json
  • --answer-redact {none,redact,drop}:控制是否输出 Answer: 行(默认 drop
  • --random-alias-per-record:记录级随机 alias,覆盖 Available tools / MCQ / function_call.name
  • --mcq-subsample <p> + --split-shards:控制 MCQ 注入比例与分 shard
  • --mcq-tag "<tag>" / --task-select-tag "<tag>" / --emit-no-mcq-tag:样式隔离与 shard 标记

验收标准(最小可测)

  • 无工具声明/无评估指令的输入上,模型不再自发输出 [MCQ] / Answer: / 评分段落。
  • 在**工具描述齐全(包含 MCQ options 干扰项)**且 alias 被随机化的场景下,仍能基于描述正确选工具/参数。

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 2

  •  
  •