Skip to content

Code blocks in model "thought" (reasoning) content are mistakenly extracted and executed by CodeExecutor #324

Description

@mjn96

问题描述 / Description

LlmAgent 配置了 code_executor 且使用支持思考/推理功能的模型(如通过 reasoning_content 返回 thinking 内容的模型)时,CodeExecutionUtils.extract_code_and_truncate_content 在从最终响应中提取代码块时,没有区分 Part.thought 标记,导致模型在"思考"阶段(reasoning/thinking)里输出的代码块,只要格式满足代码围栏规范(如 ```python ... ```),也会被当作正式代码提取出来并交给 code_executor.execute_code 真实执行。

When LlmAgent is configured with a code_executor and the underlying model supports reasoning/thinking (returns reasoning_content, wrapped into a Part with thought=True), CodeExecutionUtils.extract_code_and_truncate_content does not filter out parts marked as thought when collecting text for code-block extraction. As a result, any code fence (e.g. ```python ... ```) that appears inside the model's thinking/reasoning text is treated the same as code in the final answer, and gets actually executed by the configured code_executor.

复现步骤 / Steps to Reproduce

  1. 配置一个 LlmAgent,使用带 reasoning_content 能力的模型(例如通过 _openai_model.py 的 OpenAI-compatible reasoning 适配路径),并挂载任意 code_executor(如 UnsafeLocalCodeExecutor / ContainerCodeExecutor / 自定义 sandbox executor)。
  2. 让模型在思考阶段(reasoning)输出如下内容(只要求满足围栏格式规范):
    我先写一段示例代码验证思路:
    
    ```python
    import os
    os.system("id")  # 仅作演示,说明任意代码都可能被执行
    
  3. 观察最终事件流:即使模型的正式回复中完全没有代码块,只要 reasoning/thought 文本里有合规的代码围栏,该代码依然会被提取并调用 code_executor.execute_code(...) 真实执行。

期望行为 / Expected Behavior

thought=True 的 Part(即模型内部思考/推理过程,不代表最终确定要执行的动作)不应该被当作可执行代码的来源;只有非 thought 的正式回复文本中的代码块才应被提取执行。

实际行为 / Actual Behavior

思考内容和正式回复内容被同等对待,思考内容里的代码块会被提取并真实执行。

根因分析 / Root Cause

  1. 模型适配层(如 trpc_agent_sdk/models/_openai_model.py)把 reasoning_content 包装为普通 Part,仅通过 part.thought = True 做区分,与正文 Part 存放在同一个 content.parts 列表里:

    # trpc_agent_sdk/models/_openai_model.py
    if thought_content:
        content_part = Part.from_text(text=thought_content)
        content_part.thought = True
        parts.append(content_part)
  2. 负责提取代码块的 CodeExecutionUtils.extract_code_and_truncate_contenttrpc_agent_sdk/code_executors/utils/_code_execution.py)在收集文本时只判断 p.text 是否非空,没有排除 p.thought 为真的 Part

    # trpc_agent_sdk/code_executors/utils/_code_execution.py
    text_parts = [p for p in content.parts if p.text]
    if not text_parts:
        return code_blocks
    
    response_text = '\n'.join([p.text for p in text_parts])
    # ... 后续用 code_block_delimiters 在 response_text 中正则匹配代码块
  3. CodeExecutionResponseProcessor._run_post_processortrpc_agent_sdk/agents/core/_code_execution_processor.py)拿最终聚合事件(llm_response.partial=False)的 content 直接调用上述提取函数,提取到的代码块会立即送去执行:

    response_content = copy.deepcopy(llm_response.content)
    code_blocks = CodeExecutionUtils.extract_code_and_truncate_content(
        response_content,
        code_executor.code_block_delimiters,
        code_executor.ignore_codes,
    )
    if not code_blocks:
        return
    code_execution_result = await code_executor.execute_code(...)  # 真实执行

因此只要模型开启了推理/思考能力,且思考文本中出现合规的代码围栏,该代码就会被误当作正式指令执行,可能带来非预期的副作用(文件写入、网络请求、系统命令等),是一个潜在的安全/正确性问题。

影响范围 / Affected Version

  • Package: trpc-agent / trpc_agent_sdk
  • 复现于 0.7.90.9.0(均存在同样代码逻辑,未做修复)
  • 涉及模块:
    • trpc_agent_sdk/code_executors/utils/_code_execution.py::CodeExecutionUtils.extract_code_and_truncate_content
    • trpc_agent_sdk/agents/core/_code_execution_processor.py::_run_post_processor
    • trpc_agent_sdk/models/_openai_model.py(reasoning_content → thought=True Part 的生成路径)
    • trpc_agent_sdk/models/_anthropic_model.py(extended thinking 场景,同样受影响)

建议修复方案 / Suggested Fix

extract_code_and_truncate_content 收集 text_parts 时排除 thought 为真的 Part:

text_parts = [p for p in content.parts if p.text and not getattr(p, "thought", False)]

同时建议在 CodeExecutionResponseProcessor 或相关文档中明确说明:code_executor 只应作用于模型的最终正式回复内容,思考/推理内容不应参与代码提取与执行。

补充说明 / Additional Context

该问题在纯文本围栏匹配层面无法通过"提示词约束"完全规避——即使 System Prompt 要求模型只在正式回复里输出代码,模型的思考过程仍可能出于草稿目的写出示例代码,从框架层面过滤 thought Part 是更可靠的修复方式。

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions