{
  "canonical_name": "vitalops/datatune",
  "compilation_id": "pack_78b9a54842384e388ffccd45c7527089",
  "created_at": "2026-05-14T07:49:44.595222+00:00",
  "created_by": "project-pack-compiler",
  "feedback": {
    "carrier_selection_notes": [
      "viable_asset_types=prompt, recipe, host_instruction, eval, preflight",
      "recommended_asset_types=prompt, recipe, host_instruction, eval, preflight"
    ],
    "evidence_delta": {
      "confirmed_claims": [
        "identity_anchor_present",
        "capability_and_host_targets_present",
        "install_path_declared_or_better"
      ],
      "missing_required_fields": [],
      "must_verify_forwarded": [
        "Run or inspect `pip install datatune` in an isolated environment.",
        "Confirm the project exposes the claimed capability to at least one target host."
      ],
      "quickstart_execution_scope": "allowlisted_sandbox_smoke",
      "sandbox_command": "pip install datatune",
      "sandbox_container_image": "python:3.12-slim",
      "sandbox_execution_backend": "docker",
      "sandbox_planner_decision": "llm_execute_isolated_install",
      "sandbox_validation_id": "sbx_5d955e7d1ed94cdca2d347d8d22c4507"
    },
    "feedback_event_type": "project_pack_compilation_feedback",
    "learning_candidate_reasons": [],
    "template_gaps": []
  },
  "identity": {
    "canonical_id": "project_db3f9e061db8ff878eaabcfb5ce84017",
    "canonical_name": "vitalops/datatune",
    "homepage_url": null,
    "license": "unknown",
    "repo_url": "https://github.com/vitalops/datatune",
    "slug": "datatune",
    "source_packet_id": "phit_431dac454bf04df0be4e357f880966e1",
    "source_validation_id": "dval_aaf5c46de35c48cba1717ee2b6667f9a"
  },
  "merchandising": {
    "best_for": "需要数据分析与投资研究能力，并使用 chatgpt的用户",
    "github_forks": null,
    "github_stars": null,
    "one_liner_en": "[![PyPI version](https://img.shields.io/pypi/v/datatune.svg)](https://pypi.org/project/datatune/)",
    "one_liner_zh": "[![PyPI version](https://img.shields.io/pypi/v/datatune.svg)](https://pypi.org/project/datatune/)",
    "primary_category": {
      "category_id": "data-market-research",
      "confidence": "medium",
      "name_en": "Data & Market Research",
      "name_zh": "数据分析与投资研究",
      "reason": "matched_keywords:data"
    },
    "target_user": "使用 chatgpt 等宿主 AI 的用户",
    "title_en": "datatune",
    "title_zh": "datatune 能力包",
    "visible_tags": [
      {
        "label_en": "AI Agent Framework",
        "label_zh": "AI Agent 框架",
        "source": "repo_evidence_project_characteristics",
        "tag_id": "product_domain-ai-agent-framework",
        "type": "product_domain"
      },
      {
        "label_en": "Local AI Workspace",
        "label_zh": "本地 AI 工作台",
        "source": "repo_evidence_project_characteristics",
        "tag_id": "user_job-local-ai-workspace",
        "type": "user_job"
      },
      {
        "label_en": "Structured Extraction",
        "label_zh": "结构化提取",
        "source": "repo_evidence_project_characteristics",
        "tag_id": "core_capability-structured-extraction",
        "type": "core_capability"
      },
      {
        "label_en": "Verifiable Workflow",
        "label_zh": "可验证工作流",
        "source": "repo_evidence_project_characteristics",
        "tag_id": "workflow_pattern-verifiable-workflow",
        "type": "workflow_pattern"
      },
      {
        "label_en": "Evaluation Suite",
        "label_zh": "评测体系",
        "source": "repo_evidence_project_characteristics",
        "tag_id": "selection_signal-evaluation-suite",
        "type": "selection_signal"
      }
    ]
  },
  "packet_id": "phit_431dac454bf04df0be4e357f880966e1",
  "page_model": {
    "artifacts": {
      "artifact_slug": "datatune",
      "files": [
        "PROJECT_PACK.json",
        "QUICK_START.md",
        "PROMPT_PREVIEW.md",
        "HUMAN_MANUAL.md",
        "AI_CONTEXT_PACK.md",
        "BOUNDARY_RISK_CARD.md",
        "PITFALL_LOG.md",
        "REPO_INSPECTION.json",
        "REPO_INSPECTION.md",
        "CAPABILITY_CONTRACT.json",
        "EVIDENCE_INDEX.json",
        "CLAIM_GRAPH.json"
      ],
      "required_files": [
        "PROJECT_PACK.json",
        "QUICK_START.md",
        "PROMPT_PREVIEW.md",
        "HUMAN_MANUAL.md",
        "AI_CONTEXT_PACK.md",
        "BOUNDARY_RISK_CARD.md",
        "PITFALL_LOG.md",
        "REPO_INSPECTION.json"
      ]
    },
    "detail": {
      "capability_source": "Project Hit Packet + DownstreamValidationResult",
      "commands": [
        {
          "command": "pip install datatune",
          "label": "Python / pip · 官方安装入口",
          "source": "https://github.com/vitalops/datatune#readme",
          "verified": true
        }
      ],
      "display_tags": [
        "AI Agent 框架",
        "本地 AI 工作台",
        "结构化提取",
        "可验证工作流",
        "评测体系"
      ],
      "eyebrow": "数据分析与投资研究",
      "glance": [
        {
          "body": "判断自己是不是目标用户。",
          "label": "最适合谁",
          "value": "需要数据分析与投资研究能力，并使用 chatgpt的用户"
        },
        {
          "body": "先理解能力边界，再决定是否继续。",
          "label": "核心价值",
          "value": "[![PyPI version](https://img.shields.io/pypi/v/datatune.svg)](https://pypi.org/project/datatune/)"
        },
        {
          "body": "未完成验证前保持审慎。",
          "label": "继续前",
          "value": "publish to Doramagic.ai project surfaces"
        }
      ],
      "guardrail_source": "Boundary & Risk Card",
      "guardrails": [
        {
          "body": "Prompt Preview 只展示流程，不证明项目已安装或运行。",
          "label": "Check 1",
          "value": "不要把试用当真实运行"
        },
        {
          "body": "chatgpt",
          "label": "Check 2",
          "value": "确认宿主兼容"
        },
        {
          "body": "publish to Doramagic.ai project surfaces",
          "label": "Check 3",
          "value": "先隔离验证"
        }
      ],
      "mode": "prompt, recipe, host_instruction, eval, preflight",
      "pitfall_log": {
        "items": [
          {
            "body": "README/documentation is current enough for a first validation pass.",
            "category": "能力坑",
            "evidence": [
              "capability.assumptions | art_fcb598a7a7ed4b2482ca92474f06efe2 | https://github.com/vitalops/datatune#readme | README/documentation is current enough for a first validation pass."
            ],
            "severity": "medium",
            "suggested_check": "将假设转成下游验证清单。",
            "title": "能力判断依赖假设",
            "user_impact": "假设不成立时，用户拿不到承诺的能力。"
          },
          {
            "body": "未记录 last_activity_observed。",
            "category": "维护坑",
            "evidence": [
              "evidence.maintainer_signals | art_fcb598a7a7ed4b2482ca92474f06efe2 | https://github.com/vitalops/datatune#readme | last_activity_observed missing"
            ],
            "severity": "medium",
            "suggested_check": "补 GitHub 最近 commit、release、issue/PR 响应信号。",
            "title": "维护活跃度未知",
            "user_impact": "新项目、停更项目和活跃项目会被混在一起，推荐信任度下降。"
          },
          {
            "body": "no_demo",
            "category": "安全/权限坑",
            "evidence": [
              "downstream_validation.risk_items | art_fcb598a7a7ed4b2482ca92474f06efe2 | https://github.com/vitalops/datatune#readme | no_demo; severity=medium"
            ],
            "severity": "medium",
            "suggested_check": "进入安全/权限治理复核队列。",
            "title": "下游验证发现风险项",
            "user_impact": "下游已经要求复核，不能在页面中弱化。"
          },
          {
            "body": "No sandbox install has been executed yet; downstream must verify before user use.",
            "category": "安全/权限坑",
            "evidence": [
              "risks.safety_notes | art_fcb598a7a7ed4b2482ca92474f06efe2 | https://github.com/vitalops/datatune#readme | No sandbox install has been executed yet; downstream must verify before user use."
            ],
            "severity": "medium",
            "suggested_check": "转成明确权限清单和安全审查提示。",
            "title": "存在安全注意事项",
            "user_impact": "用户安装前需要知道权限边界和敏感操作。"
          },
          {
            "body": "no_demo",
            "category": "安全/权限坑",
            "evidence": [
              "risks.scoring_risks | art_fcb598a7a7ed4b2482ca92474f06efe2 | https://github.com/vitalops/datatune#readme | no_demo; severity=medium"
            ],
            "severity": "medium",
            "suggested_check": "把风险写入边界卡，并确认是否需要人工复核。",
            "title": "存在评分风险",
            "user_impact": "风险会影响是否适合普通用户安装。"
          },
          {
            "body": "issue_or_pr_quality=unknown。",
            "category": "维护坑",
            "evidence": [
              "evidence.maintainer_signals | art_fcb598a7a7ed4b2482ca92474f06efe2 | https://github.com/vitalops/datatune#readme | issue_or_pr_quality=unknown"
            ],
            "severity": "low",
            "suggested_check": "抽样最近 issue/PR，判断是否长期无人处理。",
            "title": "issue/PR 响应质量未知",
            "user_impact": "用户无法判断遇到问题后是否有人维护。"
          },
          {
            "body": "release_recency=unknown。",
            "category": "维护坑",
            "evidence": [
              "evidence.maintainer_signals | art_fcb598a7a7ed4b2482ca92474f06efe2 | https://github.com/vitalops/datatune#readme | release_recency=unknown"
            ],
            "severity": "low",
            "suggested_check": "确认最近 release/tag 和 README 安装命令是否一致。",
            "title": "发布节奏不明确",
            "user_impact": "安装命令和文档可能落后于代码，用户踩坑概率升高。"
          }
        ],
        "source": "ProjectPitfallLog + ProjectHitPacket + validation + community signals",
        "summary": "发现 7 个潜在踩坑项，其中 0 个为 high/blocking；最高优先级：能力坑 - 能力判断依赖假设。",
        "title": "踩坑日志"
      },
      "snapshot": {
        "contributors": null,
        "forks": null,
        "license": "unknown",
        "note": "站点快照，非实时质量证明；用于开工前背景判断。",
        "stars": null
      },
      "source_url": "https://github.com/vitalops/datatune",
      "steps": [
        {
          "body": "不安装项目，先体验能力节奏。",
          "code": "preview",
          "title": "先试 Prompt"
        },
        {
          "body": "理解输入、输出、失败模式和边界。",
          "code": "manual",
          "title": "读说明书"
        },
        {
          "body": "把上下文交给宿主 AI 继续工作。",
          "code": "context",
          "title": "带给 AI"
        },
        {
          "body": "进入主力环境前先完成安装入口与风险边界验证。",
          "code": "verify",
          "title": "沙箱验证"
        }
      ],
      "subtitle": "[![PyPI version](https://img.shields.io/pypi/v/datatune.svg)](https://pypi.org/project/datatune/)",
      "title": "datatune 能力包",
      "trial_prompt": "# datatune - Prompt Preview\n\n> 复制下面这段 Prompt 到你常用的 AI，先试一次，不需要安装。\n> 它的目标是让你直接体验这个项目的服务方式，而不是阅读项目介绍。\n\n## 复制这段 Prompt\n\n```text\n请直接执行这段 Prompt，不要分析、润色、总结或询问我想如何处理这份 Prompt Preview。\n\n你现在扮演 datatune 的“安装前体验版”。\n这不是项目介绍、不是评价报告、不是 README 总结。你的任务是让我用最小成本体验它的核心服务。\n\n我的试用任务：我想用它完成一个真实的数据分析与投资研究任务。\n我常用的宿主 AI：chatgpt\n\n【体验目标】\n围绕我的真实任务，现场演示这个项目如何把输入转成 示例引导, 判断线索。重点是让我感受到工作方式，而不是给我项目背景。\n\n【业务流约束】\n- 你必须像一个正在提供服务的项目能力包，而不是像一个讲解员。\n- 每一轮只推进一个步骤；提出问题后必须停下来等我回答。\n- 每一步都必须让我感受到一个具体服务动作：澄清、整理、规划、检查、判断或收尾。\n- 每一步都要说明：当前目标、你需要我提供什么、我回答后你会产出什么。\n- 不要安装、不要运行命令、不要写代码、不要声称测试通过、不要声称已经修改文件。\n- 需要真实安装或宿主加载后才能验证的内容，必须明确说“这一步需要安装后验证”。\n- 如果我说“用示例继续”，你可以用虚构示例推进，但仍然不能声称真实执行。\n\n【可体验服务能力】\n- 安装前能力预览: [![PyPI version](https://img.shields.io/pypi/v/datatune.svg)](https://pypi.org/project/datatune/) 输入：用户任务, 当前 AI 对话上下文；输出：示例引导, 判断线索。\n\n【必须安装后才可验证的能力】\n- 命令行启动或安装流程: 项目文档中存在可执行命令，真实使用需要在本地或宿主环境中运行这些命令。 输入：终端环境, 包管理器, 项目依赖；输出：安装结果, 列表/更新/运行结果。\n\n【核心服务流】\n请严格按这个顺序带我体验。不要一次性输出完整流程：\n1. intro：Datatune简介。围绕“Datatune简介”模拟一次用户任务，不展示安装或运行结果。\n2. quickstart：快速开始。围绕“快速开始”模拟一次用户任务，不展示安装或运行结果。\n3. architecture：系统架构。围绕“系统架构”模拟一次用户任务，不展示安装或运行结果。\n4. map-operation：Map操作。围绕“Map操作”模拟一次用户任务，不展示安装或运行结果。\n5. filter-operation：Filter操作。围绕“Filter操作”模拟一次用户任务，不展示安装或运行结果。\n\n【核心能力体验剧本】\n每一步都必须按“输入 -> 服务动作 -> 中间产物”执行。不要只说流程名：\n1. intro\n输入：用户提供的“Datatune简介”相关信息。\n服务动作：模拟项目在这一步的核心判断和整理方式。\n中间产物：一个可检查的小结果。\n\n2. quickstart\n输入：用户提供的“快速开始”相关信息。\n服务动作：模拟项目在这一步的核心判断和整理方式。\n中间产物：一个可检查的小结果。\n\n3. architecture\n输入：用户提供的“系统架构”相关信息。\n服务动作：模拟项目在这一步的核心判断和整理方式。\n中间产物：一个可检查的小结果。\n\n4. map-operation\n输入：用户提供的“Map操作”相关信息。\n服务动作：模拟项目在这一步的核心判断和整理方式。\n中间产物：一个可检查的小结果。\n\n5. filter-operation\n输入：用户提供的“Filter操作”相关信息。\n服务动作：模拟项目在这一步的核心判断和整理方式。\n中间产物：一个可检查的小结果。\n\n【项目服务规则】\n这些规则决定你如何服务用户。不要解释规则本身，而要在每一步执行时遵守：\n- 先确认用户任务、输入材料和成功标准，再模拟项目能力。\n- 每一步都必须形成可检查的小产物，并等待用户确认后再继续。\n- 凡是需要安装、调用工具或访问外部服务的能力，都必须标记为安装后验证。\n\n【每一步的服务约束】\n- Step 1 / intro：Step 1 必须围绕“Datatune简介”形成一个小中间产物，并等待用户确认。\n- Step 2 / quickstart：Step 2 必须围绕“快速开始”形成一个小中间产物，并等待用户确认。\n- Step 3 / architecture：Step 3 必须围绕“系统架构”形成一个小中间产物，并等待用户确认。\n- Step 4 / map-operation：Step 4 必须围绕“Map操作”形成一个小中间产物，并等待用户确认。\n- Step 5 / filter-operation：Step 5 必须围绕“Filter操作”形成一个小中间产物，并等待用户确认。\n\n【边界与风险】\n- 不要声称已经安装、运行、调用 API、读写本地文件或完成真实任务。\n- 安装前预览只能展示工作方式，不能证明兼容性、性能或输出质量。\n- 涉及安装、插件加载、工具调用或外部服务的能力必须安装后验证。\n\n【可追溯依据】\n这些路径只用于你内部校验或在我追问“依据是什么”时简要引用。不要在首次回复主动展开：\n- https://github.com/vitalops/datatune#readme\n- README.md\n- datatune/__init__.py\n- pyproject.toml\n- examples/Getting_started.ipynb\n- datatune/agent/agent.py\n- datatune/agent/runtime.py\n- datatune/core/map.py\n- datatune/core/filter.py\n- datatune/core/reduce.py\n- datatune/core/dask/map_dask.py\n- datatune/core/ibis/map_ibis.py\n\n【首次问题规则】\n- 首次三问必须先确认用户目标、成功标准和边界，不要提前进入工具、安装或实现细节。\n- 如果后续需要技术条件、文件路径或运行环境，必须等用户确认目标后再追问。\n\n首次回复必须只输出下面 4 个部分：\n1. 体验开始：用 1 句话说明你将带我体验 datatune 的核心服务。\n2. 当前步骤：明确进入 Step 1，并说明这一步要解决什么。\n3. 你会如何服务我：说明你会先改变我完成任务的哪个动作。\n4. 只问我 3 个问题，然后停下等待回答。\n\n首次回复禁止输出：后续完整流程、证据清单、安装命令、项目评价、营销文案、已经安装或运行的说法。\n\nStep 1 / brainstorming 的二轮协议：\n- 我回答首次三问后，你仍然停留在 Step 1 / brainstorming，不要进入 Step 2。\n- 第二次回复必须产出 6 个部分：澄清后的任务定义、成功标准、边界条件、\n  2-3 个可选方案、每个方案的权衡、推荐方案。\n- 第二次回复最后必须问我是否确认推荐方案；只有我明确确认后，才能进入下一步。\n- 第二次回复禁止输出 git worktree、代码计划、测试文件、命令或真实执行结果。\n\n后续对话规则：\n- 我回答后，你先完成当前步骤的中间产物并等待确认；只有我确认后，才能进入下一步。\n- 每一步都要生成一个小的中间产物，例如澄清后的目标、计划草案、测试意图、验证清单或继续/停止判断。\n- 所有演示都写成“我会建议/我会引导/这一步会形成”，不要写成已经真实执行。\n- 不要声称已经测试通过、文件已修改、命令已运行或结果已产生。\n- 如果某个能力必须安装后验证，请直接说“这一步需要安装后验证”。\n- 如果证据不足，请明确说“证据不足”，不要补事实。\n```\n",
      "voices": [
        {
          "body": "来源平台：github, reddit。github/github_issue: Dask's parallel computation of each partition will break true_batch_comp（https://github.com/vitalops/datatune/issues/52）；github/github_issue: Requirements Document: Planning Agent for Datatune（https://github.com/vitalops/datatune/issues/86）；github/github_issue: Requirements Doc: Unstructured data handling（https://github.com/vitalops/datatune/issues/88）；github/github_issue: Semantic Deduplication（https://github.com/vitalops/datatune/issues/91）；reddit: We built a tool to connect LLMs and Agents to the entire user data and .（https://www.reddit.com/r/LocalLLaMA/comments/1qe1mx9/cursor_for_data_we_built_a_tool_to_connect_llms/）。这些是项目级外部声音，不作为单独质量证明。",
          "items": [
            {
              "kind": "github_issue",
              "source": "github",
              "title": "Dask's parallel computation of each partition will break true_batch_comp",
              "url": "https://github.com/vitalops/datatune/issues/52"
            },
            {
              "kind": "github_issue",
              "source": "github",
              "title": "Requirements Document: Planning Agent for Datatune",
              "url": "https://github.com/vitalops/datatune/issues/86"
            },
            {
              "kind": "github_issue",
              "source": "github",
              "title": "Requirements Doc: Unstructured data handling",
              "url": "https://github.com/vitalops/datatune/issues/88"
            },
            {
              "kind": "github_issue",
              "source": "github",
              "title": "Semantic Deduplication",
              "url": "https://github.com/vitalops/datatune/issues/91"
            },
            {
              "kind": "searxng_indexed",
              "source": "reddit",
              "title": "We built a tool to connect LLMs and Agents to the entire user data and .",
              "url": "https://www.reddit.com/r/LocalLLaMA/comments/1qe1mx9/cursor_for_data_we_built_a_tool_to_connect_llms/"
            }
          ],
          "status": "已收录 5 条来源",
          "title": "社区讨论"
        }
      ]
    },
    "homepage_card": {
      "category": "数据分析与投资研究",
      "desc": "[![PyPI version](https://img.shields.io/pypi/v/datatune.svg)](https://pypi.org/project/datatune/)",
      "effort": "安装已验证",
      "forks": null,
      "icon": "chart",
      "name": "datatune 能力包",
      "risk": "可发布",
      "slug": "datatune",
      "stars": null,
      "tags": [
        "AI Agent 框架",
        "本地 AI 工作台",
        "结构化提取",
        "可验证工作流",
        "评测体系"
      ],
      "thumb": "green",
      "type": "Prompt Preview"
    },
    "manual": {
      "markdown": "# https://github.com/vitalops/datatune 项目说明书\n\n生成时间：2026-05-14 07:26:20 UTC\n\n## 目录\n\n- [Datatune简介](#intro)\n- [快速开始](#quickstart)\n- [系统架构](#architecture)\n- [数据管理与数据流](#datamanagement)\n- [Map操作](#map-operation)\n- [Filter操作](#filter-operation)\n- [Reduce操作](#reduce-operation)\n- [Agent系统](#agent-system)\n- [LLM集成](#llm-integration)\n- [数据源支持](#datasource)\n\n<a id='intro'></a>\n\n## Datatune简介\n\n### 相关页面\n\n相关主题：[系统架构](#architecture), [快速开始](#quickstart)\n\n<details>\n<summary>相关源码文件</summary>\n\n以下源码文件用于生成本页说明：\n\n- [README.md](https://github.com/vitalops/datatune/blob/main/README.md)\n- [datatune/llm/llm.py](https://github.com/vitalops/datatune/blob/main/datatune/llm/llm.py)\n- [datatune/agent/agent.py](https://github.com/vitalops/datatune/blob/main/datatune/agent/agent.py)\n- [datatune/agent/__init__.py](https://github.com/vitalops/datatune/blob/main/datatune/agent/__init__.py)\n- [datatune/core/dask/map_dask.py](https://github.com/vitalops/datatune/blob/main/datatune/core/dask/map_dask.py)\n- [datatune/core/dask/filter_dask.py](https://github.com/vitalops/datatune/blob/main/datatune/core/dask/filter_dask.py)\n- [datatune/core/ibis/map_ibis.py](https://github.com/vitalops/datatune/blob/main/datatune/core/ibis/map_ibis.py)\n- [datatune/core/ibis/filter_ibis.py](https://github.com/vitalops/datatune/blob/main/datatune/core/ibis/filter_ibis.py)\n- [datatune/core/deduplication.py](https://github.com/vitalops/datatune/blob/main/datatune/core/deduplication.py)\n- [datatune/llm/model_rate_limits.py](https://github.com/vitalops/datatune/blob/main/datatune/llm/model_rate_limits.py)\n</details>\n\n# Datatune简介\n\n## 概述\n\nDatatune是一个基于大语言模型（LLM）的数据转换框架，它允许用户使用自然语言描述来对结构化数据进行转换、映射和过滤操作。该框架支持Dask和Ibis两种后端计算引擎，能够处理大规模数据集，并通过多种LLM提供商（如OpenAI、Ollama、Azure等）实现智能化的数据处理。\n\n资料来源：[README.md:1-20]()\n\n## 核心架构\n\nDatatune的架构主要由以下几个核心组件构成：\n\n### 2.1 组件层次\n\n```mermaid\ngraph TD\n    A[用户接口层] --> B[Agent智能代理]\n    A --> C[Primitive原语操作]\n    B --> C\n    C --> D[Map映射操作]\n    C --> E[Filter过滤操作]\n    D --> F[数据后端]\n    E --> F\n    F --> G[Dask后端]\n    F --> H[Ibis后端]\n    G --> I[DuckDB/PostgreSQL/BigQuery]\n    H --> I\n    F --> J[LLM调用层]\n    J --> K[OpenAI]\n    J --> L[Ollama]\n    J --> M[Azure]\n    J --> N[Mistral]\n    J --> O[Huggingface]\n    J --> P[VLLM]\n```\n\n### 2.2 核心模块说明\n\n| 模块路径 | 功能说明 |\n|---------|---------|\n| `datatune.llm.llm` | LLM基类和多种LLM提供商实现 |\n| `datatune.agent.agent` | Agent智能代理，支持自动规划数据转换流程 |\n| `datatune.core.dask.map_dask` | Dask后端的Map映射操作实现 |\n| `datatune.core.dask.filter_dask` | Dask后端的Filter过滤操作实现 |\n| `datatune.core.ibis.map_ibis` | Ibis后端的Map映射操作实现 |\n| `datatune.core.ibis.filter_ibis` | Ibis后端的Filter过滤操作实现 |\n| `datatune.core.deduplication` | 数据去重功能，支持基于嵌入向量的相似度检测 |\n\n资料来源：[datatune/llm/llm.py:1-150]()[datatune/agent/agent.py:1-100]()\n\n## LLM支持\n\n### 3.1 支持的LLM提供商\n\nDatatune通过litellm库统一封装了多种LLM提供商，用户可以根据需求选择合适的模型：\n\n资料来源：[datatune/llm/llm.py:45-120]()\n\n| 提供商 | 类名 | 默认模型 | 特点 |\n|-------|------|---------|------|\n| OpenAI | `OpenAI` | gpt-3.5-turbo | 云端API，需提供api_key |\n| Ollama | `Ollama` | gemma3:4b | 本地部署，支持gemma、llama等模型 |\n| Azure | `Azure` | gpt-3.5-turbo | Azure OpenAI服务 |\n| Mistral | `Mistral` | mistral-tiny | Mistral AI模型 |\n| Huggingface | `Huggingface` | - | Hugging Face推理端点 |\n| VLLM | `VLLM` | 用户指定 | 高性能本地推理服务 |\n\n### 3.2 模型速率限制\n\n框架内置了主流模型的速率限制配置，包括每分钟请求数（RPM）和每分钟令牌数（TPM）：\n\n资料来源：[datatune/llm/model_rate_limits.py:1-100]()\n\n| 模型 | RPM | TPM |\n|------|-----|-----|\n| gpt-3.5-turbo | 500 | 200,000 |\n| gpt-4 | 500 | 10,000 |\n| gpt-4-turbo | 500 | 30,000 |\n| gpt-4o | 500 | 30,000 |\n| gpt-4.1-mini | 500 | 200,000 |\n| gpt-4.1-nano | 500 | 200,000 |\n\n### 3.3 LLM基类实现\n\n```python\nclass LLM:\n    def __init__(self, model_name: str, **kwargs) -> None:\n        self.model_name = model_name  # 格式为 \"provider/model\"\n        self._base_model_name = model_name.split(\"/\", 1)[1]\n```\n\n所有LLM类都继承自基类`LLM`，通过litellm库实现统一的调用接口，并支持自定义速率限制参数。\n\n资料来源：[datatune/llm/llm.py:45-70]()\n\n## 数据处理原语\n\n### 4.1 Map映射操作\n\nMap操作用于从现有数据创建新的列，通过LLM理解并应用转换逻辑：\n\n```mermaid\ngraph LR\n    A[输入DataFrame] --> B[序列化输入列]\n    B --> C[LLM批量处理]\n    C --> D[解析LLM输出]\n    D --> E[反序列化到新列]\n    E --> F[输出DataFrame]\n```\n\n**Dask实现流程：**\n\n1. 序列化输入列数据\n2. 检测重复记录并建立映射关系\n3. 仅对主记录调用LLM\n4. 复制结果到重复记录\n5. 将LLM输出反序列化为新列\n\n资料来源：[datatune/core/dask/map_dask.py:1-80]()[datatune/core/ibis/map_ibis.py:1-60]()\n\n### 4.2 Filter过滤操作\n\nFilter操作用于根据条件移除不满足要求的行：\n\n```mermaid\ngraph LR\n    A[输入DataFrame] --> B[LLM决策判断]\n    B --> C[解析过滤决策]\n    C --> D[应用过滤条件]\n    D --> E[输出过滤后DataFrame]\n```\n\n**输出格式要求：**\n\nLLM返回的过滤决策必须遵循特定格式：\n\n```\nindex=<row_index>|{key1: value1, ..., '__filter__': True/False}<endofrow>\n```\n\n资料来源：[datatune/core/dask/filter_dask.py:1-50]()[datatune/core/ibis/filter_ibis.py:1-50]()\n\n### 4.3 去重功能\n\nDatatune提供了基于嵌入向量的智能去重功能：\n\n```mermaid\ngraph TD\n    A[原始数据] --> B[嵌入向量生成]\n    B --> C[FAISS索引构建]\n    C --> D[HNSW相似度搜索]\n    D --> E[聚类分组]\n    E --> F[主记录标识]\n```\n\n**核心参数：**\n\n| 参数 | 说明 | 默认值 |\n|------|------|-------|\n| embedding_model | 嵌入模型名称 | text-embedding-3-small |\n| sim_threshold | 相似度阈值 | 0.90 |\n| top_k | Top-K近邻数 | 50 |\n| hnsw_m | HNSW图的M参数 | 32 |\n| ef_search | 搜索时的ef参数 | 64 |\n\n资料来源：[datatune/core/deduplication.py:1-150]()\n\n## Agent智能代理\n\n### 5.1 代理概述\n\nAgent是Datatune的高级抽象，它能够自动分析用户的自然语言请求，规划并执行一系列数据转换步骤：\n\n```mermaid\ngraph TD\n    A[用户自然语言请求] --> B[LLM解析意图]\n    B --> C[生成执行计划]\n    C --> D[计划验证]\n    D --> E{计划有效?}\n    E -->|是| F[逐步执行]\n    E -->|否| G[返回错误]\n    F --> H[执行日志记录]\n    H --> I[完成并返回结果]\n```\n\n资料来源：[datatune/agent/agent.py:1-150]()[datatune/agent/__init__.py:1-40]()\n\n### 5.2 计划格式\n\nAgent生成的计划为JSON数组，每个步骤包含以下字段：\n\n| 字段 | 类型 | 说明 |\n|------|------|------|\n| type | string | 操作类型：dask 或 primitive |\n| operation | string | 操作名称 |\n| params | dict | 操作参数字典 |\n| subprompt | string | 原语操作的LLM提示词 |\n| input_fields | list | 输入列列表 |\n| output_fields | list | 输出列列表（仅Map） |\n\n资料来源：[datatune/agent/agent.py:100-150]()\n\n### 5.3 支持的Dask操作\n\nAgent可使用的Dask操作包括：\n\n| 操作类型 | 操作名 | 说明 |\n|---------|--------|------|\n| 列操作 | add_column | 从表达式创建新列 |\n| 列操作 | apply_function | 对列应用函数 |\n| 列操作 | rename_columns | 重命名列 |\n| 列操作 | astype_column | 更改列数据类型 |\n\n资料来源：[datatune/agent/agent.py:150-200]()\n\n## 数据源支持\n\n### 6.1 Dask后端\n\nDask后端支持大规模分布式数据处理：\n\n```python\nimport dask.dataframe as dd\ndf = dd.read_csv(\"data.csv\")\n```\n\n特点：\n- 支持分区分布式计算\n- 延迟执行提高性能\n- 与pandas API兼容\n\n### 6.2 Ibis后端\n\nIbis后端支持多种SQL数据库：\n\n| 数据库 | 连接方式 |\n|-------|---------|\n| DuckDB | `ibis.duckdb.connect()` |\n| PostgreSQL | `ibis.postgres.connect()` |\n| BigQuery | `ibis.bigquery.connect()` |\n\n资料来源：[README.md:30-60]()\n\n## 快速开始\n\n### 7.1 安装\n\n```bash\npip install datatune\n```\n\n### 7.2 基本使用示例\n\n```python\nimport datatune as dt\nfrom datatune.llm.llm import OpenAI\nimport dask.dataframe as dd\n\nllm = OpenAI(model_name=\"gpt-3.5-turbo\")\ndf = dd.read_csv(\"products.csv\")\n\n# 使用Map创建新列\nmapped = dt.map(\n    prompt=\"从产品描述和名称中提取类别\",\n    output_fields=[\"Category\", \"Subcategory\"],\n    input_fields=[\"Description\", \"Name\"]\n)(llm, df)\n\n# 使用Filter过滤数据\nfiltered = dt.filter(\n    prompt=\"仅保留电子产品\",\n    input_fields=[\"Name\"]\n)(llm, mapped)\n\n# 保存结果\nresult = dt.finalize(filtered)\nresult.compute().to_csv(\"electronics_products.csv\")\n```\n\n### 7.3 Agent使用示例\n\n```python\nimport datatune as dt\nfrom datatune.llm.llm import OpenAI\n\nllm = OpenAI(model_name=\"gpt-3.5-turbo\")\nagent = dt.Agent(llm)\n\ndf = agent.do(\"添加ProfitMargin列并仅保留非洲组织\", df)\nresult = dt.finalize(df)\n```\n\nAgent会自动：\n- 确定使用哪些操作（map、filter等）\n- 链接多个转换步骤\n- 处理复杂的多步骤任务\n- 生成并执行Python代码\n\n资料来源：[README.md:1-100]()\n\n## 系统提示词设计\n\nAgent的系统提示词定义了可用的工具和上下文：\n\n资料来源：[datatune/agent/__init__.py:1-50]()\n\n**可用的Python库：**\n- pandas\n- numpy\n- dask\n\n**可用的数据处理原语：**\n- Map：通过LLM提示词从现有数据创建新列\n- Filter：通过LLM提示词根据条件移除行\n\n## 错误处理机制\n\n### 9.1 执行错误捕获\n\nAgent实现了完善的错误处理机制：\n\n```mermaid\ngraph TD\n    A[执行步骤] --> B{是否成功?}\n    B -->|是| C[记录日志]\n    B -->|否| D[捕获异常]\n    D --> E[格式化错误信息]\n    E --> F[返回错误和步骤号]\n```\n\n资料来源：[datatune/agent/agent.py:80-120]()\n\n### 9.2 速率限制警告\n\n当使用的模型未配置速率限制时，系统会发出警告：\n\n```python\nif \"rpm\" not in kwargs:\n    logger.warning(f\"REQUESTS-PER-MINUTE limits for model '{model_name}' not found.\")\nif \"tpm\" not in kwargs:\n    logger.warning(f\"TOKENS-PER-MINUTE limits for model '{model_name}' not found.\")\n```\n\n资料来源：[datatune/llm/llm.py:65-80]()\n\n## 总结\n\nDatatune是一个功能强大的LLM驱动数据转换框架，它：\n\n1. **简化数据处理**：通过自然语言描述替代复杂的代码编写\n2. **支持多种后端**：Dask用于大规模分布式计算，Ibis支持多种SQL数据库\n3. **集成多LLM**：OpenAI、Ollama、Azure、Mistral等主流提供商\n4. **智能去重**：基于嵌入向量的高效相似度检测\n5. **Agent自动化**：自动规划并执行复杂的数据转换流程\n\n该框架适用于需要对结构化数据进行转换、清洗和增强的各种场景，特别是当业务逻辑难以用传统编程表达时，Datatune能够显著提升开发效率。\n\n---\n\n<a id='quickstart'></a>\n\n## 快速开始\n\n### 相关页面\n\n相关主题：[Map操作](#map-operation), [Filter操作](#filter-operation), [Agent系统](#agent-system)\n\n<details>\n<summary>相关源码文件</summary>\n\n以下源码文件用于生成本页说明：\n\n- [README.md](https://github.com/vitalops/datatune/blob/main/README.md)\n- [datatune/llm/llm.py](https://github.com/vitalops/datatune/blob/main/datatune/llm/llm.py)\n- [datatune/agent/agent.py](https://github.com/vitalops/datatune/blob/main/datatune/agent/agent.py)\n- [datatune/agent/__init__.py](https://github.com/vitalops/datatune/blob/main/datatune/agent/__init__.py)\n- [datatune/core/dask/filter_dask.py](https://github.com/vitalops/datatune/blob/main/datatune/core/dask/filter_dask.py)\n- [datatune/core/dask/map_dask.py](https://github.com/vitalops/datatune/blob/main/datatune/core/dask/map_dask.py)\n- [datatune/llm/model_rate_limits.py](https://github.com/vitalops/datatune/blob/main/datatune/llm/model_rate_limits.py)\n</details>\n\n# 快速开始\n\n## 概述\n\ndatatune 是一个使用 LLM（大语言模型）进行数据转换和处理的 Python 库。它允许用户通过自然语言描述来执行复杂的数据操作任务，如数据映射、过滤和去重。快速开始指南旨在帮助用户在最短时间内上手 datatune，掌握其核心功能并完成基础的数据处理流程。\n\n该库支持多种 LLM 后端，包括 OpenAI、Ollama、Azure、Mistral、Huggingface 和 VLLM，同时兼容 Dask 和 Ibis（支持 DuckDB、PostgreSQL、BigQuery 等）数据源。资料来源：[README.md](https://github.com/vitalops/datatune/blob/main/README.md)\n\n## 环境准备\n\n### 安装方式\n\ndatatune 可通过 pip 直接安装：\n\n```bash\npip install datatune\n```\n\n资料来源：[README.md](https://github.com/vitalops/datatune/blob/main/README.md)\n\n### LLM 配置\n\ndatatune 支持多种 LLM 提供商，需根据使用的模型进行相应配置：\n\n| LLM 类型 | 初始化方式 | 默认模型 | 必需参数 |\n|---------|-----------|---------|---------|\n| OpenAI | `OpenAI(model_name=\"gpt-3.5-turbo\")` | gpt-3.5-turbo | api_key（可选） |\n| Ollama | `Ollama()` | gemma3:4b | api_base（默认 localhost:11434） |\n| Azure | `Azure(model_name=\"...\")` | - | api_key, api_base, api_version |\n| Mistral | `Mistral(model_name=\"mistral/mistral-tiny\")` | mistral/mistral-tiny | api_key |\n| Huggingface | `Huggingface(model_name=\"...\")` | - | api_key |\n| VLLM | `VLLM(model_name=\"...\")` | - | api_base |\n\n资料来源：[datatune/llm/llm.py](https://github.com/vitalops/datatune/blob/main/datatune/llm/llm.py)\n\n## 核心工作流程\n\ndatatune 的数据处理遵循标准的三步流程：加载数据、执行转换、最终化结果。以下是完整的工作流程图：\n\n```mermaid\ngraph TD\n    A[读取数据源] --> B[map 或 filter 转换]\n    B --> C{datatune.finalize]\n    C --> D[执行 compute]\n    D --> E[保存结果]\n    \n    F[LLM 实例] --> B\n```\n\n## 数据加载\n\n### Dask 数据源\n\n使用 Dask 读取 CSV 文件：\n\n```python\nimport dask.dataframe as dd\n\ndf = dd.read_csv(\"products.csv\")\n```\n\n### Ibis 数据源\n\n使用 Ibis 连接 DuckDB 或其他数据库：\n\n```python\nimport ibis\n\ncon = ibis.duckdb.connect(\"data.duckdb\")\ntable = con.table(\"my_table\")\n```\n\n资料来源：[README.md](https://github.com/vitalops/datatune/blob/main/README.md)\n\n## map 操作\n\n`dt.map()` 用于从现有列生成新的列，通过自然语言描述转换逻辑。\n\n```python\nimport datatune as dt\n\nmapped = dt.map(\n    prompt=\"Extract categories from the description and name of product.\",\n    output_fields=[\"Category\", \"Subcategory\"],\n    input_fields=[\"Description\", \"Name\"]\n)(llm, df)\n```\n\n### 参数说明\n\n| 参数 | 类型 | 说明 |\n|-----|------|-----|\n| prompt | str | 自然语言描述转换规则 |\n| output_fields | List[str] | 输出新列名列表 |\n| input_fields | List[str] | 输入列名列表 |\n\n资料来源：[README.md](https://github.com/vitalops/datatune/blob/main/README.md)\n\n### 实现原理\n\nmap 操作内部通过 LLM 调用处理数据，将每行数据序列化为字典格式，附加到提示词中发送给 LLM，LLM 返回包含新字段的字典。实现文件位于 `datatune/core/dask/map_dask.py` 和 `datatune/core/ibis/map_ibis.py`。\n\nmap_dask.py 中的提示词格式如下：\n\n```python\nsuffix = (\n    f\"{os.linesep}{os.linesep}\"\n    \"Your response MUST be the entire input record as a valid Python dictionary in the format\"\n    \"'index=<row_index>|{key1: value1, key2: value2, ...}'  with added keys of expected new fields if any.\"\n)\n```\n\n资料来源：[datatune/core/dask/map_dask.py](https://github.com/vitalops/datatune/blob/main/datatune/core/dask/map_dask.py)\n\n## filter 操作\n\n`dt.filter()` 用于根据条件过滤数据行。\n\n```python\nfiltered = dt.filter(\n    prompt=\"Keep only electronics products\",\n    input_fields=[\"Name\"]\n)(llm, mapped)\n```\n\n### 参数说明\n\n| 参数 | 类型 | 说明 |\n|-----|------|-----|\n| prompt | str | 自然语言描述过滤条件 |\n| input_fields | List[str] | 输入列名列表 |\n\n资料来源：[README.md](https://github.com/vitalops/datatune/blob/main/README.md)\n\n### 实现原理\n\nfilter 操作使用特殊格式让 LLM 判断每行是否应保留。返回格式包含 `__filter__` 键，值为 `True` 表示保留，`False` 表示移除。\n\n```python\nsuffix = (\n    f\"{os.linesep}{os.linesep}\"\n    \"DECISION:Your response MUST be the entire input record as a Python dictionary...\"\n    \"with added key called '__filter__' with value either True to KEEP the record or False to REMOVE it.\"\n)\n```\n\n资料来源：[datatune/core/dask/filter_dask.py](https://github.com/vitalops/datatune/blob/main/datatune/core/dask/filter_dask.py)\n\n## 最终化与保存结果\n\n使用 `dt.finalize()` 完成所有计算并将结果转换为标准 DataFrame：\n\n```python\nresult = dt.finalize(filtered)\nresult.compute().to_csv(\"electronics_products.csv\")\n```\n\n`finalize()` 函数负责触发 Dask 计算图的实际执行，将延迟操作转换为最终结果。资料来源：[README.md](https://github.com/vitalops/datatune/blob/main/README.md)\n\n## Agent 模式\n\n对于复杂的、多步骤的数据处理任务，可使用 `dt.Agent` 让 AI 自动规划转换步骤：\n\n```python\nimport datatune as dt\nfrom datatune.llm.llm import OpenAI\n\nllm = OpenAI(model_name=\"gpt-3.5-turbo\")\nagent = dt.Agent(llm)\n\ndf = dd.read_csv(\"data.csv\")\n\n# 描述复杂需求，Agent 自动规划执行步骤\ndf = agent.do(\"Add ProfitMargin column and keep only African organizations\", df)\nresult = dt.finalize(df)\n```\n\n### Agent 自动完成的工作\n\n| 功能 | 说明 |\n|-----|------|\n| 操作类型判断 | 自动决定使用 map、filter 还是其他操作 |\n| 步骤链式执行 | 自动串联多个转换步骤 |\n| 复杂任务处理 | 支持从单一提示词执行多步操作 |\n| 代码生成 | 生成 Python 代码和行级原语操作 |\n\n资料来源：[README.md](https://github.com/vitalops/datatune/blob/main/README.md)\n\n### Agent 系统提示词\n\nAgent 基于以下系统提示词工作：\n\n```\nYou are Datatune Agent, a powerful assistant designed to help users with data processing tasks.\nYou are capable of generating python code to perform various operations on data. Apart from python builtins, you have the following libraries avaiable in your runtime:\n- pandas\n- numpy\n- dask\n```\n\n资料来源：[datatune/agent/__init__.py](https://github.com/vitalops/datatune/blob/main/datatune/agent/__init__.py)\n\n## 模型速率限制\n\ndatatune 内置了常见 OpenAI 模型的速率限制配置，确保 API 调用不会超出限制：\n\n| 模型 | TPM（每分钟令牌数） | RPM（每分钟请求数） |\n|-----|---------------------|---------------------|\n| gpt-3.5-turbo | 200,000 | 500 |\n| gpt-4 | 10,000 | 500 |\n| gpt-4-turbo | 30,000 | 500 |\n| gpt-4.1 | 30,000 | 500 |\n| gpt-4.1-mini | 200,000 | 500 |\n| gpt-4.1-nano | 200,000 | 500 |\n| gpt-4o | 30,000 | 500 |\n\n资料来源：[datatune/llm/model_rate_limits.py](https://github.com/vitalops/datatune/blob/main/datatune/llm/model_rate_limits.py)\n\n## 完整示例\n\n以下是一个完整的快速开始示例，整合所有核心功能：\n\n```python\nimport datatune as dt\nfrom datatune.llm.llm import OpenAI\nimport dask.dataframe as dd\n\n# 1. 初始化 LLM\nllm = OpenAI(model_name=\"gpt-3.5-turbo\")\n\n# 2. 加载数据\ndf = dd.read_csv(\"products.csv\")\n\n# 3. 使用 map 提取类别\nmapped = dt.map(\n    prompt=\"Extract categories from the description and name of product.\",\n    output_fields=[\"Category\", \"Subcategory\"],\n    input_fields=[\"Description\", \"Name\"]\n)(llm, df)\n\n# 4. 使用 filter 筛选电子产品\nfiltered = dt.filter(\n    prompt=\"Keep only electronics products\",\n    input_fields=[\"Name\"]\n)(llm, mapped)\n\n# 5. 最终化并保存\nresult = dt.finalize(filtered)\nresult.compute().to_csv(\"electronics_products.csv\")\n```\n\n## 后续步骤\n\n- 查看完整文档：https://docs.datatune.ai/\n- 参考示例代码：https://github.com/vitalops/datatune/tree/main/examples\n- 加入社区 Discord：https://discord.gg/3RKA5AryQX\n\n---\n\n<a id='architecture'></a>\n\n## 系统架构\n\n### 相关页面\n\n相关主题：[Agent系统](#agent-system), [LLM集成](#llm-integration), [数据源支持](#datasource)\n\n<details>\n<summary>相关源码文件</summary>\n\n以下源码文件用于生成本页说明：\n\n- [datatune/llm/llm.py](https://github.com/vitalops/datatune/blob/main/datatune/llm/llm.py)\n- [datatune/agent/agent.py](https://github.com/vitalops/datatune/blob/main/datatune/agent/agent.py)\n- [datatune/core/dask/map_dask.py](https://github.com/vitalops/datatune/blob/main/datatune/core/dask/map_dask.py)\n- [datatune/core/dask/filter_dask.py](https://github.com/vitalops/datatune/blob/main/datatune/core/dask/filter_dask.py)\n- [datatune/core/deduplication.py](https://github.com/vitalops/datatune/blob/main/datatune/core/deduplication.py)\n- [datatune/__init__.py](https://github.com/vitalops/datatune/blob/main/datatune/__init__.py)\n</details>\n\n# 系统架构\n\n## 概述\n\nDatatune 是一个基于大语言模型（LLM）的数据处理框架，其核心设计目标是通过自然语言描述实现对数据的转换、过滤和去重操作。系统采用模块化架构，将 LLM 交互、数据处理后端和智能代理三个层面解耦，支持 Dask 和 Ibis 两种数据处理后端，兼容 DuckDB、PostgreSQL、BigQuery 等多种数据源。\n\n资料来源：[datatune/__init__.py:1-7]()\n\n## 架构分层\n\nDatatune 采用三层架构设计，从下至上依次为：\n\n```mermaid\ngraph TD\n    subgraph \"表现层\"\n        Agent\n    end\n    \n    subgraph \"核心操作层\"\n        Map\n        Filter\n        Reduce\n    end\n    \n    subgraph \"后端抽象层\"\n        Dask后端\n        Ibis后端\n    end\n    \n    subgraph \"LLM交互层\"\n        OpenAI\n        Ollama\n        VLLM\n        Azure\n    end\n    \n    Agent --> Map\n    Agent --> Filter\n    Map --> Dask后端\n    Map --> Ibis后端\n    Filter --> Dask后端\n    Filter --> Ibis后端\n    OpenAI --> Agent\n    Ollama --> Agent\n    VLLM --> Agent\n    Azure --> Agent\n```\n\n### 各层职责\n\n| 层级 | 组件 | 职责 |\n|------|------|------|\n| 表现层 | Agent | 理解用户意图，规划执行步骤，生成并执行代码 |\n| 核心操作层 | Map/Filter/Reduce | 提供声明式数据转换接口 |\n| 后端抽象层 | Dask/Ibis | 实现分布式数据处理逻辑 |\n| LLM交互层 | 各LLM实现类 | 封装与不同语言模型的通信协议 |\n\n资料来源：[datatune/agent/agent.py:1-15]()\n\n## LLM 交互层\n\n### 抽象基类设计\n\n`LLM` 是所有 LLM 提供者的抽象基类，通过 `litellm` 库实现统一的模型调用接口：\n\n```python\nclass LLM:\n    def __init__(self, model_name: str, **kwargs) -> None:\n        self.model_name = model_name  # 格式: \"provider/model\"\n        self._base_model_name = model_name.split(\"/\", 1)[1]\n```\n\n资料来源：[datatune/llm/llm.py:17-24]()\n\n### 支持的 LLM 提供者\n\n| 提供者 | 类名 | 默认模型 | 特点 |\n|--------|------|----------|------|\n| OpenAI | `OpenAI` | gpt-3.5-turbo | 云端 API |\n| Ollama | `Ollama` | gemma3:4b | 本地部署 |\n| VLLM | `VLLM` | 需指定 | 高性能推理 |\n| Azure | `Azure` | 需指定 | 企业级部署 |\n\n资料来源：[datatune/llm/llm.py:65-108]()\n\n### 模型速率限制\n\n系统内置了主流模型的速率限制配置，包含每分钟请求数（RPM）和每分钟令牌数（TPM）：\n\n```python\nmodel_rate_limits = {\n    \"gpt-3.5-turbo\": {\"tpm\": 200_000, \"rpm\": 500},\n    \"gpt-4\": {\"tpm\": 10_000, \"rpm\": 500},\n    \"gpt-4o\": {\"tpm\": 30_000, \"rpm\": 500},\n}\n```\n\n资料来源：[datatune/llm/model_rate_limits.py:1-20]()\n\n## 核心操作层\n\n### Map 操作\n\nMap 操作通过 LLM 将现有列转换为新列，实现数据转换功能：\n\n```mermaid\ngraph LR\n    A[输入DataFrame] --> B[序列化输入列]\n    B --> C[LLM批量推理]\n    C --> D[解析输出字典]\n    D --> E[写入新列]\n    E --> F[输出DataFrame]\n```\n\n关键流程包括：\n1. 将输入列序列化为字符串格式\n2. 调用 LLM 批量处理，根据 prompt 生成映射结果\n3. 解析 LLM 返回的 Python 字典格式输出\n4. 将结果写入新生成的列\n\n资料来源：[datatune/core/dask/map_dask.py:1-50]()\n\n### Filter 操作\n\nFilter 操作根据 LLM 生成的决策条件过滤数据行：\n\n```mermaid\ngraph LR\n    A[输入DataFrame] --> B[序列化输入列]\n    B --> C[LLM决策判断]\n    C --> D[解析__filter__字段]\n    D --> E[布尔索引过滤]\n    E --> F[输出DataFrame]\n```\n\nLLM 响应格式要求包含 `__filter__` 字段，值为 `True` 表示保留，`False` 表示移除：\n\n```\nindex=0|{key1: value1, key2: value2, '__filter__': True}<endofrow>\n```\n\n资料来源：[datatune/core/dask/filter_dask.py:1-60]()\n\n### 语义去重\n\n`SemanticDeduplicator` 使用嵌入向量和 FAISS 索引实现语义级别的去重：\n\n```mermaid\ngraph TD\n    A[原始数据] --> B[嵌入生成]\n    B --> C[FAISS HNSW索引]\n    C --> D[流式聚类]\n    D --> E[重复组识别]\n    E --> F[规范ID映射]\n```\n\n去重器支持以下参数：\n\n| 参数 | 说明 | 默认值 |\n|------|------|--------|\n| embedding_model | 嵌入模型名称 | text-embedding-3-small |\n| sim_threshold | 相似度阈值 | 0.90 |\n| top_k | 最近邻数量 | 50 |\n| hnsw_m | HNSW 索引参数 | 32 |\n| ef_search | 搜索精度参数 | 64 |\n\n资料来源：[datatune/core/deduplication.py:1-80]()\n\n## Agent 代理层\n\n### Agent 执行流程\n\nAgent 是系统的智能编排层，能够自动分析用户需求并生成执行计划：\n\n```mermaid\ngraph TD\n    A[用户自然语言描述] --> B[LLM生成执行计划]\n    B --> C{计划步骤循环}\n    C -->|步骤类型为dask| D[执行Dask操作模板]\n    C -->|步骤类型为primitive| E[执行Primitive操作]\n    D --> F[记录步骤日志]\n    E --> F\n    F --> G{还有更多步骤?}\n    G -->|是| C\n    G -->|否| H[计算并返回结果]\n```\n\n### 执行计划结构\n\nAgent 生成的执行计划是一个 JSON 数组，每个步骤包含以下字段：\n\n| 字段 | 类型 | 说明 |\n|------|------|------|\n| type | string | \"dask\" 或 \"primitive\" |\n| operation | string | 操作名称 |\n| params | dict | Dask 模板参数 |\n| subprompt | string | Primitive 操作的 LLM 提示词 |\n| input_fields | list | 输入列名 |\n| output_fields | list | 输出列名（仅 Map） |\n\n资料来源：[datatune/agent/agent.py:80-120]()\n\n### 操作模板\n\nAgent 支持两种类型的操作模板：\n\n#### Dask 操作模板\n\n用于直接生成 Dask DataFrame 操作代码：\n\n```python\nTEMPLATE = {\n    \"dask\": {\n        \"add_column\": \"df = df.assign({new_column}=...)\",\n        \"group_by\": \"df = df.groupby(...).agg(...)\",\n        \"rename_columns\": \"df = df.rename(columns={mapping})\",\n    }\n}\n```\n\n#### Primitive 操作模板\n\n用于通过 LLM 生成逐行转换逻辑：\n\n```python\nTEMPLATE = {\n    \"primitive\": {\n        \"Map\": \"df = dt.Map(subprompt=...)\",\n        \"Filter\": \"df = dt.Filter(subprompt=...)\",\n    }\n}\n```\n\n资料来源：[datatune/agent/agent.py:25-50]()\n\n## 数据流处理\n\n### Dask 分区处理\n\n系统使用 `map_partitions` 实现分布式数据处理，保证每个分区独立执行 LLM 操作：\n\n```python\ncode_lines += [\n    f\"df = df.map_partitions(log_primitive, {start_msg!r}, meta=df._meta)\",\n    \"step_num += 1\",\n    template,\n    f\"df = df.map_partitions(log_primitive, {end_msg!r}, meta=df._meta)\"\n]\n```\n\n资料来源：[datatune/agent/agent.py:55-75]()\n\n### 重复记录处理\n\n在 Map 和 Filter 操作中，系统采用规范 ID 映射策略处理重复记录：\n\n```python\ndup_to_canon = {\n    dup: c[\"canonical_id\"]\n    for c in clusters\n    for dup in c[\"duplicate_ids\"]\n}\n\ncanonical_idx = input_series.index.difference(dup_to_canon.keys())\ncanonical_input = input_series.loc[canonical_idx]\nllm_out = llm(canonical_input, ...)\n\n# 将结果回填到重复记录\nfor dup, canon in dup_to_canon.items():\n    df.loc[dup, llm_output_column] = df.loc[canon, llm_output_column]\n```\n\n资料来源：[datatune/core/dask/map_dask.py:30-55]()\n\n## 公共 API\n\n系统通过 `datatune/__init__.py` 导出以下核心组件：\n\n```python\nfrom datatune.agent.agent import Agent\nfrom datatune.core.filter import filter\nfrom datatune.core.map import map\nfrom datatune.core.dask.op import finalize\nfrom datatune.core.deduplication import SemanticDeduplicator\nfrom datatune.core.reduce import reduce\n\n__all__ = [\"map\", \"filter\", \"finalize\", \"Agent\", \"reduce\"]\n```\n\n资料来源：[datatune/__init__.py:1-10]()\n\n## 错误处理机制\n\n### 步骤级错误追踪\n\nAgent 在执行计划时维护步骤计数器，失败时返回错误信息和失败步骤编号：\n\n```python\ndef _execute_plan(self, plan: List[Dict]):\n    self.runtime.update({\"step_num\": 0, \"plan\": plan})\n    for i, step in enumerate(plan, start=1):\n        try:\n            # 执行步骤...\n            self.runtime[\"step_num\"] = i\n        except Exception as e:\n            return str(e), i - 1\n```\n\n资料来源：[datatune/agent/agent.py:45-70]()\n\n### 输出解析容错\n\nLLM 输出解析使用 `ast.literal_eval` 进行安全评估，解析失败时返回空字典：\n\n```python\ndef safe_parse(row):\n    try:\n        return ast.literal_eval(row)\n    except Exception:\n        return {}\n```\n\n资料来源：[datatune/core/deduplication.py:60-65]()\n\n## 总结\n\nDatatune 的系统架构体现了以下设计原则：\n\n1. **层次化解耦**：LLM 交互、数据处理和用户接口三层分离，便于维护和扩展\n2. **多后端支持**：通过 Dask 和 Ibis 抽象，支持从单机到分布式多种部署场景\n3. **智能编排**：Agent 层自动规划执行路径，降低用户使用门槛\n4. **容错设计**：完善的错误处理和日志记录机制，便于问题诊断\n5. **语义去重**：基于嵌入向量的语义匹配，实现精准的数据去重\n\n---\n\n<a id='datamanagement'></a>\n\n## 数据管理与数据流\n\n### 相关页面\n\n相关主题：[数据源支持](#datasource), [Map操作](#map-operation), [Filter操作](#filter-operation)\n\n<details>\n<summary>相关源码文件</summary>\n\n以下源码文件用于生成本页说明：\n\n- [datatune/core/dask/map_dask.py](https://github.com/vitalops/datatune/blob/main/datatune/core/dask/map_dask.py)\n- [datatune/core/dask/filter_dask.py](https://github.com/vitalops/datatune/blob/main/datatune/core/dask/filter_dask.py)\n- [datatune/core/ibis/map_ibis.py](https://github.com/vitalops/datatune/blob/main/datatune/core/ibis/map_ibis.py)\n- [datatune/core/ibis/filter_ibis.py](https://github.com/vitalops/datatune/blob/main/datatune/core/ibis/filter_ibis.py)\n- [datatune/core/deduplication.py](https://github.com/vitalops/datatune/blob/main/datatune/core/deduplication.py)\n- [datatune/agent/agent.py](https://github.com/vitalops/datatune/blob/main/datatune/agent/agent.py)\n- [datatune/core/reduce.py](https://github.com/vitalops/datatune/blob/main/datatune/core/reduce.py)\n- [datatune/llm/llm.py](https://github.com/vitalops/datatune/blob/main/datatune/llm/llm.py)\n</details>\n\n# 数据管理与数据流\n\n## 概述\n\nDatatune 是一个基于 LLM 的数据处理库，通过自然语言提示词实现数据的映射（Map）、过滤（Filter）和去重（Deduplication）操作。系统支持多种后端数据源，包括 Dask DataFrame 和 Ibis（支持 DuckDB、PostgreSQL、BigQuery 等），并通过统一的 API 接口实现数据流的透明处理。\n\n核心设计理念是将复杂的数据转换逻辑封装为可组合的操作符，用户只需通过自然语言描述期望的转换结果，系统即可自动生成并执行相应的处理流程。\n\n## 架构概览\n\n```mermaid\ngraph TD\n    subgraph \"数据输入层\"\n        CSV[CSV 文件]\n        SQLDB[(SQL 数据库)]\n        DuckDB[(DuckDB)]\n    end\n    \n    subgraph \"抽象接口层\"\n        DD[Dask DataFrame]\n        IB[Ibis Table]\n    end\n    \n    subgraph \"核心操作层\"\n        MAP[Map 操作]\n        FILTER[Filter 操作]\n        DEDUP[去重操作]\n        REDUCE[Reduce 操作]\n    end\n    \n    subgraph \"LLM 服务层\"\n        OPENAI[OpenAI]\n        OLLAMA[Ollama]\n        AZURE[Azure]\n        VLLM[VLLM]\n    end\n    \n    CSV --> DD\n    SQLDB --> IB\n    DuckDB --> IB\n    \n    DD --> MAP\n    DD --> FILTER\n    DD --> DEDUP\n    IB --> MAP\n    IB --> FILTER\n    \n    MAP --> LLM\n    FILTER --> LLM\n    \n    LLM --> OPENAI\n    LLM --> OLLAMA\n    LLM --> AZURE\n    LLM --> VLLM\n```\n\n## 数据源支持\n\nDatatune 通过抽象数据类型检测机制自动选择合适的后端实现。\n\n### 支持的数据源\n\n| 数据源类型 | 后端实现 | 导入方式 |\n|-----------|---------|---------|\n| CSV/Parquet | Dask | `dask.dataframe as dd` |\n| DuckDB | Ibis | `ibis.duckdb.connect()` |\n| PostgreSQL | Ibis | `ibis.postgres.connect()` |\n| BigQuery | Ibis | `ibis.bigquery.connect()` |\n\n### 数据类型检测\n\n数据类型检测通过 `__init__.py` 中的内部函数实现：\n\n```python\ndef _is_ibis_table(obj):\n    import ibis\n    return isinstance(obj, ibis.Table)\n\ndef _is_dask_df(obj):\n    import dask.dataframe as dd\n    return isinstance(obj, dd.DataFrame)\n```\n\n资料来源：[datatune/core/map.py:1-16](https://github.com/vitalops/datatune/blob/main/datatune/core/map.py)\n\n## Map 操作数据流\n\nMap 操作是 Datatune 的核心功能之一，用于根据自然语言提示词生成新的数据列。\n\n### 数据处理流程\n\n```mermaid\ngraph LR\n    A[输入 DataFrame] --> B[序列化输入列]\n    B --> C[去重预处理]\n    C --> D[批量调用 LLM]\n    D --> E[解析 LLM 输出]\n    E --> F[合并去重结果]\n    F --> G[输出包含新列的 DataFrame]\n```\n\n### Dask 后端 Map 实现\n\nDask 后端的 Map 操作通过 `_map_dask` 函数实现，采用延迟计算和分区处理策略：\n\n```python\ndef _map_dask(prompt, output_fields, input_fields=None, clusters=None):\n    # 1. 序列化输入列为 JSON 字符串\n    serialized_input_column = \"serialized_input\"\n    df[serialized_input_column] = df[input_fields].apply(\n        lambda x: json.dumps(x.to_dict()), axis=1, meta=(serialized_input_column, str)\n    )\n    \n    # 2. 构建 LLM 调用前缀和后缀\n    prefix = \"...\"\n    suffix = \"Your response MUST be the entire input record...\"\n    \n    # 3. 处理重复数据的预处理\n    dup_to_canon = {\n        dup: c[\"canonical_id\"]\n        for c in clusters\n        for dup in c[\"duplicate_ids\"]\n    }\n    canonical_idx = input_series.index.difference(dup_to_canon.keys())\n    canonical_input = input_series.loc[canonical_idx]\n    \n    # 4. 批量调用 LLM\n    llm_out = llm(canonical_input, prefix, prompt, suffix, optimized=True)\n    \n    # 5. 将 LLM 结果写回 DataFrame\n    df.loc[canonical_idx, llm_output_column] = llm_out\n    \n    # 6. 复制去重结果到重复行\n    for dup, canon in dup_to_canon.items():\n        df.loc[dup, llm_output_column] = df.loc[canon, llm_output_column]\n```\n\n资料来源：[datatune/core/dask/map_dask.py:1-45](https://github.com/vitalops/datatune/blob/main/datatune/core/dask/map_dask.py)\n\n### Ibis 后端 Map 实现\n\nIbis 后端采用不同的实现方式，使用行号索引和内存表进行数据处理：\n\n```python\ndef _map_ibis(table, llm, input_col, output_col, prompt, expected_new_fields):\n    # 添加行号索引\n    indexed_table = table.mutate(_ROW_ID_=ibis.row_number().cast(\"int64\"))\n    \n    # 将数据拉到本地执行\n    local_data = indexed_table.select(\"_ROW_ID_\", input_col).execute()\n    input_list = local_data[input_col].tolist()\n    \n    # 调用 LLM\n    raw_results = llm(input_list, prefix, mapping_prompt, suffix, optimized=True)\n    \n    # 解析结果并处理异常\n    processed_results = []\n    for res in raw_results:\n        try:\n            py_dict = ast.literal_eval(str(res).strip())\n            processed_results.append(json.dumps(py_dict))\n        except Exception:\n            processed_results.append(\"{}\")\n    \n    # 创建内存表并关联\n    mapping_df = pd.DataFrame({\n        \"_ROW_ID_\": local_data[\"_ROW_ID_\"].values, \n        output_col: processed_results\n    })\n    mapping_table = ibis.memtable(mapping_df)\n```\n\n资料来源：[datatune/core/ibis/map_ibis.py:1-50](https://github.com/vitalops/datatune/blob/main/datatune/core/ibis/map_ibis.py)\n\n### LLM 输出格式规范\n\nMap 操作要求 LLM 返回特定格式的输出：\n\n```\nindex=<row_index>|{key1: value1, key2: value2, ...}\n```\n\n- 必须以 `index=<row_index>|` 开头\n- 主体必须是有效的 Python 字典格式\n- 新增字段必须使用双引号\n- 缺失值使用 `None` 表示\n\n资料来源：[datatune/core/dask/map_dask.py:15-25](https://github.com/vitalops/datatune/blob/main/datatune/core/dask/map_dask.py)\n\n## Filter 操作数据流\n\nFilter 操作根据自然语言条件过滤数据行。\n\n### 过滤处理流程\n\n```mermaid\ngraph LR\n    A[输入 DataFrame] --> B[序列化数据行]\n    B --> C[构建过滤提示词]\n    C --> D[批量调用 LLM]\n    D --> E[解析过滤决策]\n    E --> F[应用过滤掩码]\n    F --> G[输出过滤后的 DataFrame]\n```\n\n### Dask 后端 Filter 实现\n\n```python\ndef _filter_dask(df, llm, input_fields, prompt, clusters=None):\n    # 序列化输入数据\n    serialized_input_column = \"serialized_input\"\n    df[serialized_input_column] = df[input_fields].apply(\n        lambda x: json.dumps(x.to_dict()), axis=1, meta=(serialized_input_column, str)\n    )\n    \n    # 构建过滤提示词\n    suffix = (\n        f\"{os.linesep}{os.linesep}\"\n        \"DECISION:Your response MUST be the entire input record...\"\n        \"with added key called '__filter__' with value either True to KEEP \"\n        \"the record or False to REMOVE it.\"\n    )\n    \n    # 去重处理\n    dup_to_canon = {\n        dup: c[\"canonical_id\"]\n        for c in clusters\n        for dup in c[\"duplicate_ids\"]\n    }\n    canonical_idx = input_series.index.difference(dup_to_canon.keys())\n    canonical_input = input_series.loc[canonical_idx]\n    \n    # 调用 LLM 获取过滤决策\n    llm_out = llm(canonical_input, prefix, prompt, suffix, optimized=True)\n    \n    # 合并去重结果\n    for dup, canon in dup_to_canon.items():\n        df.loc[dup, llm_output_column] = df.loc[canon, llm_output_column]\n```\n\n资料来源：[datatune/core/dask/filter_dask.py:1-40](https://github.com/vitalops/datatune/blob/main/datatune/core/dask/filter_dask.py)\n\n### LLM 过滤决策格式\n\nFilter 操作要求 LLM 返回带有 `__filter__` 键的字典：\n\n```\nindex=<row_index>|{key1: value1, key2: value2, ..., '__filter__': True}\n```\n\n- `__filter__: True` 表示保留该行\n- `__filter__: False` 表示移除该行\n\n资料来源：[datatune/core/dask/filter_dask.py:18-24](https://github.com/vitalops/datatune/blob/main/datatune/core/dask/filter_dask.py)\n\n## 去重机制\n\nDatatune 实现了基于语义相似度的去重功能，用于优化 LLM 调用并保持数据一致性。\n\n### 去重处理架构\n\n```mermaid\ngraph TD\n    A[输入分区数据] --> B[Embedding 向量化]\n    B --> C[构建 FAISS HNSW 索引]\n    C --> D[相似度搜索]\n    D --> E[聚类重复数据]\n    E --> F[合并重复行结果]\n```\n\n### 去重处理流程\n\n```python\ndef _embed_and_write_partition(self, part, partition_id, output_dir):\n    # 解析行为字典\n    dicts = [safe_parse(row) for row in pdf]\n    \n    # 构建文本表示\n    texts = [\n        \", \".join(f\"{k}: {v}\" for k, v in d.items())\n        for d in dicts\n    ]\n    \n    # 批量生成 Embedding\n    for i in range(0, len(texts), 256):\n        batch = texts[i:i+256]\n        resp = embedding(model=self.embedding_model, input=batch)\n        embeddings.extend([item[\"embedding\"] for item in resp[\"data\"]])\n    \n    # 归一化并存储\n    X = np.asarray(embeddings, dtype=\"float32\")\n    faiss.normalize_L2(X)\n    np.save(f\"{output_dir}/embeddings_part_{partition_id}.npy\", X)\n```\n\n资料来源：[datatune/core/deduplication.py:1-60](https://github.com/vitalops/datatune/blob/main/datatune/core/deduplication.py)\n\n### 去重配置参数\n\n| 参数 | 默认值 | 说明 |\n|-----|-------|------|\n| `embedding_model` | `text-embedding-3-small` | 用于向量化的 Embedding 模型 |\n| `sim_threshold` | `0.90` | 相似度阈值，超过此值视为重复 |\n| `top_k` | `50` | 搜索时返回的最近邻数量 |\n| `hnsw_m` | `32` | HNSW 索引参数 M |\n| `ef_search` | `64` | HNSW 搜索参数 |\n\n资料来源：[datatune/core/deduplication.py:1-25](https://github.com/vitalops/datatune/blob/main/datatune/core/deduplication.py)\n\n## Reduce 操作\n\nReduce 操作通过注册-执行模式实现数据的聚合转换。\n\n### 架构设计\n\n```mermaid\ngraph LR\n    A[定义 Action] --> B[注册到全局表]\n    B --> C[调用 reduce 函数]\n    C --> D[查找并实例化 Action]\n    D --> E[执行数据处理]\n```\n\n### Action 注册机制\n\n```python\n_ACTIONS = {}\n\ndef register_action(name):\n    def decorator(cls):\n        _ACTIONS[name] = cls\n        return cls\n    return decorator\n\ndef get_action(name):\n    try:\n        return _ACTIONS[name]\n    except KeyError:\n        raise ValueError(f\"Unknown action: {name}\")\n\ndef reduce(df, *, action: str, **kwargs):\n    cls = get_action(action)\n    reducer = cls(**kwargs)   \n    return reducer(df)\n```\n\n资料来源：[datatune/core/reduce.py:1-25](https://github.com/vitalops/datatune/blob/main/datatune/core/reduce.py)\n\n## Agent 编排层\n\nAgent 是 Datatune 的智能编排组件，能够根据用户自然语言描述自动规划并执行数据处理流程。\n\n### Agent 工作流\n\n```mermaid\ngraph TD\n    A[用户描述任务] --> B[规划器分析意图]\n    B --> C{判断操作类型}\n    C -->|需要语义理解| D[使用 Primitive 操作]\n    C -->|纯数据转换| E[使用 Dask 操作]\n    D --> F[执行 Map/Filter]\n    E --> G[执行 add_column/group_by 等]\n    F --> H[合并结果]\n    G --> H\n    H --> I[返回处理后的 DataFrame]\n```\n\n### 计划执行流程\n\n```python\ndef _execute_plan(self, plan: List[Dict]):\n    self._set_df(self.df)\n    runtime.update({\"step_num\": 0, \"plan\": plan})\n    \n    for i, step in enumerate(plan, start=1):\n        step_type = step[\"type\"]\n        operation = step[\"operation\"]\n        params = step[\"params\"]\n        \n        if step_type == \"dask\":\n            template = self.TEMPLATE[\"dask\"][step[\"operation\"]].format(**params)\n            self.runtime.execute(template + \"\\n_ = df.head()\")\n        elif step_type == \"primitive\":\n            template = self.TEMPLATE[\"primitive\"][step[\"operation\"]].format(**params)\n            self.runtime.execute(template)\n```\n\n资料来源：[datatune/agent/agent.py:1-80](https://github.com/vitalops/datatune/blob/main/datatune/agent/agent.py)\n\n### 支持的操作类型\n\n#### Primitive 操作\n\n| 操作类型 | 说明 | 参数 |\n|---------|------|------|\n| `Map` | 从文本生成新列 | `subprompt`, `input_fields`, `output_fields` |\n| `Filter` | 根据条件过滤行 | `subprompt`, `input_fields` |\n\n#### Dask 操作\n\n| 操作类型 | 说明 | 参数 |\n|---------|------|------|\n| `add_column` | 从表达式添加列 | `new_column`, `expression` |\n| `group_by_agg` | 分组聚合 | `group_columns`, `aggregations` |\n| `rename_columns` | 重命名列 | `mapping` |\n| `astype_column` | 改变列类型 | `column`, `dtype` |\n\n资料来源：[datatune/agent/agent.py:50-100](https://github.com/vitalops/datatune/blob/main/datatune/agent/agent.py)\n\n## LLM 接口层\n\nLLM 模块提供了统一的接口，支持多种大语言模型服务。\n\n### 支持的 LLM 提供商\n\n| 提供商 | 类名 | 默认模型 | 配置参数 |\n|-------|------|---------|---------|\n| OpenAI | `OpenAI` | `gpt-3.5-turbo` | `api_key` |\n| Ollama | `Ollama` | `gemma3:4b` | `api_base` |\n| Azure | `Azure` | 自定义 | `api_key`, `api_base`, `api_version` |\n| VLLM | `VLLM` | 自定义 | `api_base`, `max_tokens` |\n\n### 批处理机制\n\nLLM 模块实现了智能批处理，根据输入长度动态分组请求以优化 API 调用：\n\n```python\n# token 计数估算\nprefix_suffix_tokens = token_counter(model_name, messages=message(\"\"))\ntotal_ntokens = prefix_suffix_tokens\n\nfor i, prompt in enumerate(input_rows):\n    # 动态批处理逻辑\n    # 当累计 token 接近限制时执行当前批次\n```\n\n资料来源：[datatune/llm/llm.py:1-60](https://github.com/vitalops/datatune/blob/main/datatune/llm/llm.py)\n\n## API 参考\n\n### 核心函数\n\n#### map 函数\n\n```python\ndef map(*, prompt, output_fields, input_fields=None, clusters=None):\n    def apply(llm, data):\n        if _is_dask_df(data):\n            from .dask.map_dask import _map_dask\n            return _map_dask(prompt=prompt, output_fields=output_fields, \n                           input_fields=input_fields, clusters=clusters)(llm, data)\n        elif _is_ibis_table(data):\n            from .ibis.map_ibis import _map_ibis\n            return _map_ibis(prompt=prompt, output_fields=output_fields,\n                            input_fields=input_fields)(llm, data)\n    return apply\n```\n\n资料来源：[datatune/core/map.py:20-38](https://github.com/vitalops/datatune/blob/main/datatune/core/map.py)\n\n#### filter 函数\n\n```python\ndef filter(*, prompt, input_fields=None, clusters=None):\n    def apply(llm, data):\n        if _is_dask_df(data):\n            from .dask.filter_dask import _filter_dask\n            return _filter_dask(prompt=prompt, input_fields=input_fields,\n                               clusters=clusters)(llm, data)\n        elif _is_ibis_table(data):\n            from .ibis.filter_ibis import _filter_ibis\n            return _filter_ibis(prompt=prompt, input_fields=input_fields)(llm, data)\n    return apply\n```\n\n资料来源：[datatune/core/filter.py:20-36](https://github.com/vitalops/datatune/blob/main/datatune/core/filter.py)\n\n### 模块导出\n\n```python\nfrom datatune import map, filter, finalize, Agent, reduce\n```\n\n资料来源：[datatune/__init__.py:1-8](https://github.com/vitalops/datatune/blob/main/datatune/__init__.py)\n\n## 完整使用示例\n\n```python\nimport datatune as dt\nfrom datatune.llm.llm import OpenAI\nimport dask.dataframe as dd\n\n# 初始化 LLM\nllm = OpenAI(model_name=\"gpt-3.5-turbo\")\n\n# 读取数据\ndf = dd.read_csv(\"products.csv\")\n\n# Map: 提取分类信息\nmapped = dt.map(\n    prompt=\"Extract categories from the description and name of product.\",\n    output_fields=[\"Category\", \"Subcategory\"],\n    input_fields=[\"Description\", \"Name\"]\n)(llm, df)\n\n# Filter: 保留电子产品\nfiltered = dt.filter(\n    prompt=\"Keep only electronics products\",\n    input_fields=[\"Name\"]\n)(llm, mapped)\n\n# 获取最终结果\nresult = dt.finalize(filtered)\nresult.compute().to_csv(\"electronics_products.csv\")\n\n---\n\n<a id='map-operation'></a>\n\n## Map操作\n\n### 相关页面\n\n相关主题：[Filter操作](#filter-operation), [Reduce操作](#reduce-operation)\n\n<details>\n<summary>相关源码文件</summary>\n\n以下源码文件用于生成本页说明：\n\n- [datatune/core/map.py](https://github.com/vitalops/datatune/blob/main/datatune/core/map.py)\n- [datatune/core/dask/map_dask.py](https://github.com/vitalops/datatune/blob/main/datatune/core/dask/map_dask.py)\n- [datatune/core/ibis/map_ibis.py](https://github.com/vitalops/datatune/blob/main/datatune/core/ibis/map_ibis.py)\n- [datatune/llm/llm.py](https://github.com/vitalops/datatune/blob/main/datatune/llm/llm.py)\n- [datatune/agent/agent.py](https://github.com/vitalops/datatune/blob/main/datatune/agent/agent.py)\n</details>\n\n# Map操作\n\n## 概述\n\nMap操作是datatune库的核心功能之一，它通过大型语言模型（LLM）实现对数据列的语义转换和增强。用户只需提供自然语言描述的转换规则，Map操作即可自动解析数据、调用LLM生成新字段，并返回增强后的数据集。\n\n该操作支持Dask和Ibis两种后端，能够处理分布式数据源（如CSV、数据库表等），同时内置去重优化机制，避免对语义相同的记录重复调用LLM API，从而显著降低使用成本。\n\n## 架构设计\n\n### 模块层次\n\n```\ndatatune.core.map (顶层接口)\n    ├── datatune.core.dask.map_dask (Dask后端实现)\n    └── datatune.core.ibis.map_ibis (Ibis后端实现)\n```\n\n### 执行流程\n\n```mermaid\ngraph TD\n    A[开始Map操作] --> B[序列化输入列]\n    B --> C{启用去重?}\n    C -->|是| D[构建重复记录映射]\n    C -->|否| E[跳过去重]\n    D --> F[提取唯一记录索引]\n    E --> G[调用LLM API]\n    F --> G\n    G --> H[解析LLM输出]\n    H --> I[填充结果到DataFrame]\n    I --> J[返回增强后的数据]\n    D --> K[复制结果到重复记录]\n    K --> J\n```\n\n## 核心实现\n\n### Dask后端 map_dask.py\n\n#### 去重优化机制\n\nDask后端的Map操作实现了智能去重功能，通过`SemanticDeduplicator`预先识别语义相似的记录。当存在重复记录群组时，系统仅对每个群组的**canonical（规范）记录**调用LLM，然后将结果复制到群组内的其他重复记录。\n\n```python\ndup_to_canon = {\n    dup: c[\"canonical_id\"]\n    for c in clusters\n    for dup in c[\"duplicate_ids\"]\n}\n\ncanonical_idx = input_series.index.difference(dup_to_canon.keys())\ncanonical_input = input_series.loc[canonical_idx]\n\nllm_out = llm(\n    canonical_input,\n    prefix,\n    prompt,\n    suffix,\n    optimized=True\n)\n\ndf.loc[canonical_idx, llm_output_column] = llm_out\n\nfor dup, canon in dup_to_canon.items():\n    df.loc[dup, llm_output_column] = df.loc[canon, llm_output_column]\n```\n\n资料来源：[datatune/core/dask/map_dask.py:25-46](https://github.com/vitalops/datatune/blob/main/datatune/core/dask/map_dask.py)\n\n#### LLM调用与输出解析\n\nMap操作使用特定的输出格式约束，确保LLM返回可解析的Python字典：\n\n```python\nsuffix = (\n    f\"{os.linesep}{os.linesep}\"\n    \"Your response MUST be the entire input record as a valid Python dictionary in the format\"\n    \"'index=<row_index>|{key1: value1, key2: value2, ...}'  with added keys of expected new fields if any.\"\n     \n    \"ALWAYS START YOUR RESPONSE WITH 'index=<row_index>|' WHERE <row_index> IS THE INDEX OF THE ROW.\" \\\n    \"IF A VALUE FOR A COLUMN DOES NOT EXIST SET IT TO null\" \\\n    \"'index=<row_index>|{key1: None, key2: value2, ...}'\"\n)\n```\n\n输出解析通过`parse_llm_output`函数处理，将LLM返回的字符串转换为Python字典对象。\n\n资料来源：[datatune/core/dask/map_dask.py:8-17](https://github.com/vitalops/datatune/blob/main/datatune/core/dask/map_dask.py)\n\n### Ibis后端 map_ibis.py\n\n#### 行号索引机制\n\nIbis后端为每个输入记录添加行号索引，以便后续结果关联：\n\n```python\nindexed_table = table.mutate(_ROW_ID_=ibis.row_number().cast(\"int64\"))\n\nlocal_data = indexed_table.select(\"_ROW_ID_\", input_col).execute()\n\ninput_list = local_data[input_col].tolist()\n\nraw_results = llm(input_list, prefix, mapping_prompt, suffix, optimized=True)\n```\n\n资料来源：[datatune/core/ibis/map_ibis.py:35-45](https://github.com/vitalops/datatune/blob/main/datatune/core/ibis/map_ibis.py)\n\n#### 结果转换与关联\n\n解析后的结果被转换为内存表并与原表JOIN：\n\n```python\nprocessed_results = []\nfor res in raw_results:\n    try:\n        py_dict = ast.literal_eval(str(res).strip())\n        processed_results.append(json.dumps(py_dict))\n    except Exception:\n        processed_results.append(\"{}\")\n\nmapping_df = pd.DataFrame({\n    \"_ROW_ID_\": local_data[\"_ROW_ID_\"].values, \n    output_col: processed_results\n})\nmapping_table = ibis.memtable(mapping_df)\n```\n\n资料来源：[datatune/core/ibis/map_ibis.py:47-60](https://github.com/vitalops/datatune/blob/main/datatune/core/ibis/map_ibis.py)\n\n## Agent集成\n\n### Plan定义格式\n\n在Agent的Plan中，Map操作作为primitive步骤声明：\n\n```json\n{\n    \"type\": \"primitive\",\n    \"operation\": \"map\",\n    \"params\": {\n        \"subprompt\": \"Extract category and sub-category from industry\",\n        \"input_fields\": [\"Industry\"],\n        \"output_fields\": [\"Category\", \"Sub-Category\"]\n    }\n}\n```\n\n资料来源：[datatune/agent/agent.py:78-85](https://github.com/vitalops/datatune/blob/main/datatune/agent/agent.py)\n\n### 步骤类型决策规则\n\nAgent根据以下规则选择Map操作：\n\n| 条件 | 选择操作类型 |\n|------|-------------|\n| 需要自然语言理解、语义提取、分类 | primitive (Map/Filter) |\n| 涉及多列语义推理 | primitive (Map) |\n| 简单的数值/列操作 | dask |\n| 复杂多步骤转换 | 优先使用dask，后续步骤依赖新列时可使用primitive |\n\n## 使用示例\n\n### 基础用法\n\n```python\nimport datatune as dt\nfrom datatune.llm.llm import OpenAI\nimport dask.dataframe as dd\n\nllm = OpenAI(model_name=\"gpt-3.5-turbo\")\ndf = dd.read_csv(\"products.csv\")\n\n# 提取类别信息\nmapped = dt.map(\n    prompt=\"Extract categories from the description and name of product.\",\n    output_fields=[\"Category\", \"Subcategory\"],\n    input_fields=[\"Description\", \"Name\"]\n)(llm, df)\n\n# 完成转换并保存\nresult = dt.finalize(mapped)\nresult.compute().to_csv(\"mapped_products.csv\")\n```\n\n### 与Agent结合使用\n\n```python\nimport datatune as dt\nfrom datatune.llm.llm import OpenAI\n\nllm = OpenAI(model_name=\"gpt-3.5-turbo\")\nagent = dt.Agent(llm)\n\ndf = agent.do(\"Add ProfitMargin column and keep only African organizations\", df)\nresult = dt.finalize(df)\n```\n\n## API参数说明\n\n### dt.map 函数参数\n\n| 参数 | 类型 | 必填 | 说明 |\n|------|------|------|------|\n| prompt | str | 是 | 自然语言描述的转换规则 |\n| input_fields | List[str] | 是 | 输入列名列表 |\n| output_fields | List[str] | 否 | 新生成的输出列名列表 |\n| optimized | bool | 否 | 是否启用去重优化，默认为True |\n\n### LLM类配置\n\ndatatune支持多种LLM后端，各后端的初始化参数如下：\n\n| LLM类 | 主要参数 | 默认值 |\n|-------|---------|--------|\n| OpenAI | model_name, api_key | gpt-3.5-turbo |\n| Ollama | model_name, api_base | gemma3:4b, http://localhost:11434 |\n| Azure | model_name, api_key, api_base, api_version | - |\n| VLLM | model_name, api_base | - , http://localhost:8000/v1 |\n\n资料来源：[datatune/llm/llm.py:48-95](https://github.com/vitalops/datatune/blob/main/datatune/llm/llm.py)\n\n## 速率限制\n\ndatatune内置了常用模型的速率限制配置，支持tpm（每分钟Token数）和rpm（每分钟请求数）两个维度的控制。\n\n| 模型 | TPM | RPM |\n|------|-----|-----|\n| gpt-3.5-turbo | 200,000 | 500 |\n| gpt-4 | 10,000 | 500 |\n| gpt-4-turbo | 30,000 | 500 |\n| gpt-4.1-mini | 200,000 | 500 |\n\n资料来源：[datatune/llm/model_rate_limits.py:1-30](https://github.com/vitalops/datatune/blob/main/datatune/llm/model_rate_limits.py)\n\n## 技术特性\n\n### 语义去重优化\n\n当数据中存在语义重复的记录时，Map操作通过`SemanticDeduplicator`识别并合并：\n\n```mermaid\ngraph LR\n    A[原始数据] --> B[语义去重]\n    B --> C[Canonical记录]\n    B --> D[重复记录映射]\n    C --> E[单次LLM调用]\n    D --> F[结果复制]\n    E --> G[最终结果]\n    F --> G\n```\n\n此机制可节省高达50%-90%的API调用成本，同时保持结果准确性。\n\n### 分布式处理\n\nMap操作基于Dask或Ibis实现，支持大规模数据集的分布式处理：\n\n- **Dask后端**：利用Dask DataFrame的分区机制，每个分区独立处理\n- **Ibis后端**：支持DuckDB、PostgreSQL、BigQuery等多种数据库后端\n\n## 最佳实践\n\n1. **批量输入优化**：尽量将相关记录放在一起处理，提高去重效果\n2. **明确输出字段**：在`output_fields`中清晰指定新列名，便于后续操作\n3. **选择合适模型**：简单提取任务可使用gpt-3.5-turbo，复杂推理建议使用gpt-4\n4. **结果验证**：首次使用新prompt时建议抽样验证输出质量\n\n## 相关模块\n\n- **Filter操作** (`datatune/core/filter.py`)：基于LLM的过滤功能\n- **Agent** (`datatune/agent/agent.py`)：自动规划并执行Map/Filter/dask操作\n- **SemanticDeduplicator** (`datatune/core/deduplication.py`)：语义去重功能\n- **finalize** (`datatune/core/dask/op.py`)：完成Lazy DataFrame计算\n\n---\n\n<a id='filter-operation'></a>\n\n## Filter操作\n\n### 相关页面\n\n相关主题：[Map操作](#map-operation), [Reduce操作](#reduce-operation)\n\n<details>\n<summary>相关源码文件</summary>\n\n以下源码文件用于生成本页说明：\n\n- [datatune/core/filter.py](https://github.com/vitalops/datatune/blob/main/datatune/core/filter.py)\n- [datatune/core/dask/filter_dask.py](https://github.com/vitalops/datatune/blob/main/datatune/core/dask/filter_dask.py)\n- [datatune/core/ibis/filter_ibis.py](https://github.com/vitalops/datatune/blob/main/datatune/core/ibis/filter_ibis.py)\n</details>\n\n# Filter操作\n\n## 概述\n\nFilter操作是datatune库中用于基于自然语言描述对数据进行行级过滤的核心功能。该操作允许用户使用LLM（大型语言模型）根据语义理解来判断哪些数据行应该被保留或移除，无需编写复杂的条件表达式。\n\nFilter操作的本质是将用户的自然语言过滤条件翻译为布尔判断，通过LLM的语义理解能力实现智能化的数据筛选。系统会在每条数据记录中添加一个`__filter__`字段，值为`True`表示保留该记录，`False`表示移除该记录。\n\n资料来源：[datatune/core/filter.py:1-30]()\n\n## 架构设计\n\n### 模块结构\n\nFilter操作采用后端分发的设计模式，根据输入数据的类型自动选择相应的处理后端。顶层模块`filter.py`负责类型检测和后端路由，具体实现分布在Dask和Ibis两个后端模块中。\n\n```mermaid\ngraph TD\n    A[用户调用 dt.filter] --> B{数据类型检测}\n    B -->|Dask DataFrame| C[_filter_dask]\n    B -->|Ibis Table| D[_filter_ibis]\n    B -->|不支持的类型| E[TypeError]\n    \n    C --> F[LLM批量推理]\n    D --> F\n    \n    F --> G[解析LLM输出]\n    G --> H[生成过滤序列]\n    H --> I[执行过滤操作]\n```\n\n资料来源：[datatune/core/filter.py:16-29]()\n\n### 后端支持\n\n| 后端类型 | 处理模块 | 支持的数据源 |\n|---------|---------|-------------|\n| Dask | `datatune/core/dask/filter_dask.py` | Dask DataFrame |\n| Ibis | `datatune/core/ibis/filter_ibis.py` | Ibis Table（DuckDB、PostgreSQL、BigQuery等） |\n\n资料来源：[datatune/core/filter.py:18-27]()\n\n## API接口\n\n### 函数签名\n\n```python\ndef filter(*, prompt, input_fields=None, clusters=None):\n    def apply(llm, data):\n        # 后端分发逻辑\n        pass\n    return apply\n```\n\n### 参数说明\n\n| 参数 | 类型 | 必填 | 说明 |\n|-----|------|------|------|\n| `prompt` | str | 是 | 自然语言描述的过滤条件 |\n| `input_fields` | List[str] | 否 | 要参与过滤判断的输入列（当前版本中未实际使用） |\n| `clusters` | List[Dict] | 否 | 用于去重场景的聚类信息，避免重复调用LLM |\n\n资料来源：[datatune/core/filter.py:12-28]()\n\n### 使用示例\n\n```python\nimport datatune as dt\nfrom datatune.llm.llm import OpenAI\nimport dask.dataframe as dd\n\nllm = OpenAI(model_name=\"gpt-3.5-turbo\")\ndf = dd.read_csv(\"products.csv\")\n\n# 使用自然语言过滤数据\nfiltered = dt.filter(\n    prompt=\"Keep only electronics products\",\n    input_fields=[\"Name\"]\n)(llm, df)\n\nresult = dt.finalize(filtered)\n```\n\n资料来源：[README.md:1-45]()\n\n## 工作流程\n\n### 整体流程\n\nFilter操作的处理流程分为以下几个阶段：\n\n```mermaid\ngraph LR\n    A[序列化输入数据] --> B[构建LLM提示词]\n    B --> C[批量调用LLM]\n    C --> D[解析LLM输出]\n    D --> E[提取__filter__字段]\n    E --> F[生成布尔序列]\n    F --> G[执行数据过滤]\n```\n\n### 提示词构建\n\n系统自动构建包含前缀、用户提示和后缀的完整提示词。\n\n**前缀部分**包含输入数据格式规范：\n```\nEach answer must be formatted exactly as 'index=<index>|{answer}<endofrow>'.\n{answer} must be any requested python literal (e.g. list, dict, string, integer)\neg: for a dict answer: index=1|{'key1': 'value1', 'key2': 2}<endofrow>\nEnsure that each response is a valid python literal.\nEnsure that strings are enclosed in double quotes.\nSTRINGS MUST BE ENCLOSED IN DOUBLE QUOTES. DO NOT OUTPUT BARE TEXT\n```\n\n**用户提示部分**包含过滤条件：\n```\nFILTERING CRITERIA:\n{prompt}\n```\n\n**后缀部分**指定输出格式要求：\n```\nDECISION: Your response MUST be the entire input record as Python dictionary in the format: index=<row_index>|{key1: value1, key2: value2, ...}<endofrow> with added key called '__filter__' with value either True to KEEP the record or False to REMOVE it.\nNo explanations or additional text.\nALWAYS STICK TO THE FORMAT index=<row_index>|{key1: value1, key2: value2, ...}<endofrow> with added key called '__filter__' with value either True to KEEP the record or False to REMOVE it.\nIF A VALUE FOR A COLUMN DOES NOT EXIST SET IT TO None\n```\n\n资料来源：[datatune/core/dask/filter_dask.py:1-30]()\n\n### LLM响应格式\n\nLLM返回的数据必须包含原始记录信息，并添加`__filter__`布尔字段：\n\n```python\n# 示例输入\n{\"Name\": \"Laptop\", \"Price\": 999, \"Category\": \"Electronics\"}\n\n# LLM期望输出格式\n'index=0|{\"Name\": \"Laptop\", \"Price\": 999, \"Category\": \"Electronics\", \"__filter__\": true}<endofrow>'\n```\n\n## Dask后端实现\n\n### 去重机制\n\nDask后端支持基于聚类的去重优化。当提供`clusters`参数时，系统会建立一个从重复ID到规范ID的映射：\n\n```python\ndup_to_canon = {\n    dup: c[\"canonical_id\"]\n    for c in clusters\n    for dup in c[\"duplicate_ids\"]\n}\n\ncanonical_idx = input_series.index.difference(dup_to_canon.keys())\ncanonical_input = input_series.loc[canonical_idx]\n```\n\n对于规范数据行调用LLM进行判断，然后将结果复制到所有重复行：\n\n```python\nfor dup, canon in dup_to_canon.items():\n    df.loc[dup, llm_output_column] = df.loc[canon, llm_output_column]\n```\n\n资料来源：[datatune/core/dask/filter_dask.py:25-50]()\n\n### 过滤序列生成\n\n使用`parse_filter_output`函数解析LLM输出：\n\n```python\ndef parse_filter_output(\n    output: Union[str, Exception], err: bool = True\n) -> Optional[bool]:\n    # 解析LLM返回的字典，提取__filter__字段值\n```\n\n资料来源：[datatune/core/dask/filter_dask.py:60-75]()\n\n## Ibis后端实现\n\n### 行号索引\n\nIbis后端通过`row_number()`函数为表添加行号索引：\n\n```python\nindexed_table = table.mutate(_ROW_ID_=ibis.row_number().cast(\"int64\"))\nlocal_data = indexed_table.select(\"_ROW_ID_\", input_col).execute()\n```\n\n资料来源：[datatune/core/ibis/filter_ibis.py:20-25]()\n\n### 数据处理\n\nIbis实现同样通过AST解析提取过滤决策，并将结果存储在内存表中进行关联：\n\n```python\nmapping_df = pd.DataFrame({\n    \"_ROW_ID_\": local_data[\"_ROW_ID_\"].values, \n    output_col: processed_results\n})\nmapping_table = ibis.memtable(mapping_df)\n```\n\n资料来源：[datatune/core/ibis/filter_ibis.py:35-45]()\n\n## 与Agent的集成\n\nFilter操作可以与datatune的Agent功能结合使用，让AI自动判断何时需要使用Filter操作：\n\n```python\nagent = dt.Agent(llm)\ndf = agent.do(\"Keep only African organizations\", df)\n```\n\n在Agent的规划模板中，Filter操作被定义为primitive类型：\n\n```python\n\"filter\": textwrap.dedent(\n    \"\"\"\\\n    filtered = dt.filter(\n        prompt=\"{subprompt}\",\n        input_fields={input_fields}\n    )(llm, df)\n    df = filtered\n    \"\"\"\n)\n```\n\n资料来源：[datatune/agent/agent.py:100-110]()\n\n## 错误处理\n\n| 错误类型 | 处理方式 |\n|---------|---------|\n| 不支持的数据类型 | 抛出`TypeError`，提示当前支持Dask和Ibis |\n| LLM输出解析失败 | 捕获异常，返回空字典或默认值 |\n| API调用异常 | 通过异常处理机制传递给上层 |\n\n资料来源：[datatune/core/filter.py:27-28]()\n\n## 最佳实践\n\n### 输入数据准备\n\n确保输入数据包含足够的上下文信息供LLM进行判断。对于涉及多列条件的过滤，应在prompt中明确说明需要参考的列。\n\n### 批量处理\n\nFilter操作支持批量处理，通过设置合理的批次大小来平衡处理速度和API调用频率。\n\n### 去重优化\n\n在处理可能包含重复记录的数据时，使用`clusters`参数可以显著减少LLM API调用次数，提升处理效率。\n\n---\n\n<a id='reduce-operation'></a>\n\n## Reduce操作\n\n### 相关页面\n\n相关主题：[Map操作](#map-operation), [Filter操作](#filter-operation)\n\n<details>\n<summary>相关源码文件</summary>\n\n以下源码文件用于生成本页说明：\n\n- [datatune/core/reduce.py](https://github.com/vitalops/datatune/blob/main/datatune/core/reduce.py)\n- [datatune/__init__.py](https://github.com/vitalops/datatune/blob/main/datatune/__init__.py)\n- [datatune/core/dask/map_dask.py](https://github.com/vitalops/datatune/blob/main/datatune/core/dask/map_dask.py)\n- [datatune/core/dask/filter_dask.py](https://github.com/vitalops/datatune/blob/main/datatune/core/dask/filter_dask.py)\n- [datatune/core/deduplication.py](https://github.com/vitalops/datatune/blob/main/datatune/core/deduplication.py)\n</details>\n\n# Reduce操作\n\n## 概述\n\nReduce操作是datatune库中用于对数据执行聚合、归约和转换操作的核心模块之一。该模块采用插件化架构设计，允许通过注册机制扩展不同的归约操作类型。`reduce`函数作为统一入口，接收数据帧和操作类型参数，动态调用对应的归约处理器完成数据处理任务。\n\n在datatune的生态系统定位中，Reduce操作与Map、Filter操作共同构成了基于大语言模型的数据处理管道。三者的职责分工如下：\n\n| 操作类型 | 职责 | 输入 | 输出 |\n|---------|------|------|------|\n| Map | 字段映射与转换 | 单行数据 | 添加新字段的行数据 |\n| Filter | 数据过滤 | 单行数据 | 布尔决策（保留/移除） |\n| Reduce | 聚合与归约 | 多行数据 | 聚合结果 |\n\n## 核心架构\n\n### 插件注册机制\n\nReduce模块采用工厂模式和装饰器模式实现插件化架构。核心组件包括全局动作注册表、动作注册装饰器和动作获取函数三个部分。\n\n```mermaid\ngraph TD\n    A[reduce函数] --> B[get_action获取动作类]\n    B --> C{查找_ACTIONS注册表}\n    C -->|找到| D[实例化动作类]\n    C -->|未找到| E[抛出ValueError异常]\n    D --> F[调用reducer执行归约]\n    F --> G[返回处理结果]\n    \n    H[register_action装饰器] --> I[将动作类注册到_ACTIONS]\n    J[自定义动作类] --> H\n    K[内置动作类] --> H\n```\n\n### 全局动作注册表\n\n```python\n_ACTIONS = {}\n```\n\n`_ACTIONS`是一个模块级字典，用作动作类的注册表。键为动作名称字符串，值为对应的动作类。资料来源：[datatune/core/reduce.py:1]()\n\n### 注册装饰器\n\n```python\ndef register_action(name):\n    def decorator(cls):\n        _ACTIONS[name] = cls\n        return cls\n    return decorator\n```\n\n`register_action`装饰器接收动作名称作为参数，返回一个接受类作为参数的装饰器函数。装饰器执行时将类注册到`_ACTIONS`字典中，并返回原类以保持装饰器链的正常工作。资料来源：[datatune/core/reduce.py:6-12]()\n\n### 动作获取函数\n\n```python\ndef get_action(name):\n    try:\n        return _ACTIONS[name]\n    except KeyError:\n        raise ValueError(f\"Unknown action: {name}\")\n```\n\n`get_action`函数从注册表中查找指定名称的动作类。若未找到对应动作，抛出`ValueError`异常并提示未知动作名称。资料来源：[datatune/core/reduce.py:14-20]()\n\n## API参考\n\n### reduce函数\n\n```python\ndef reduce(df, *, action: str, **kwargs):\n    cls = get_action(action)\n    reducer = cls(**kwargs)\n    return reducer(df)\n```\n\n**函数签名参数说明：**\n\n| 参数 | 类型 | 必填 | 说明 |\n|------|------|------|------|\n| `df` | `dask.dataframe.DataFrame` | 是 | 输入数据帧 |\n| `action` | `str` | 是 | 动作类型名称，用于从注册表获取对应动作类 |\n| `**kwargs` | 任意 | 否 | 传递给动作类构造器的关键字参数 |\n\n**返回值：** 返回动作类实例调用后的结果，具体类型取决于具体动作的实现。\n\n资料来源：[datatune/core/reduce.py:23-27]()\n\n## 使用方式\n\n### 基本调用模式\n\n```python\nimport datatune as dt\nimport dask.dataframe as dd\n\n# 读取数据\ndf = dd.read_csv(\"data.csv\")\n\n# 调用reduce操作\nresult = dt.reduce(df, action=\"your_action_name\", param1=value1, param2=value2)\n```\n\n### 在管道中使用\n\nReduce操作可以与其他datatune操作链式调用，构建完整的数据处理管道：\n\n```python\nimport datatune as dt\nfrom datatune.llm.llm import OpenAI\n\nllm = OpenAI(model_name=\"gpt-3.5-turbo\")\n\n# 构建处理管道\ndf = dd.read_csv(\"raw_data.csv\")\nmapped = dt.map(prompt=\"提取产品类别\", input_fields=[\"description\"], output_fields=[\"category\"])(llm, df)\nfiltered = dt.filter(prompt=\"只保留电子产品\", input_fields=[\"category\"])(llm, mapped)\nresult = dt.reduce(df, action=\"deduplicate\", embedding_model=\"text-embedding-3-small\")\nfinal_result = dt.finalize(result)\n```\n\n## 内置归约动作\n\n### SemanticDeduplicator\n\n语义去重动作，用于识别和合并语义相似的记录。该动作使用嵌入向量计算记录间的语义相似度，适用于需要处理近似重复数据的场景。\n\n**嵌入到磁盘功能：**\n\n```python\ndef embed_column_to_disk(self, df, column, output_dir):\n    os.makedirs(output_dir, exist_ok=True)\n\n    tasks = []\n\n    for pid in range(df.npartitions):\n        part = df[column].get_partition(pid)\n        task = dask.delayed(self._embed_and_write_partition)(\n            part,\n            pid,\n            output_dir,\n        )\n        tasks.append(task)\n\n    dask.compute(*tasks)\n```\n\n资料来源：[datatune/core/deduplication.py:1-100]()\n\n**分区嵌入处理逻辑：**\n\n```python\ndef _embed_and_write_partition(self, part, partition_id, output_dir):\n    pdf = part\n\n    if pdf.empty:\n        print(\"empty partition\")\n        return 0\n\n    emb_path = f\"{output_dir}/embeddings_part_{partition_id}.npy\"\n    idx_path = f\"{output_dir}/index_part_{partition_id}.npy\"\n\n    if os.path.exists(emb_path) and os.path.exists(idx_path):\n        return 0\n\n    row_index = pdf.index.to_numpy()\n\n    def safe_parse(row):\n        try:\n            return ast.literal_eval(row)\n        except Exception:\n            return {}\n\n    dicts = [safe_parse(row) for row in pdf]\n\n    texts = [\n        \", \".join(f\"{k}: {v}\" for k, v in d.items())\n        for d in dicts\n    ]\n\n    embeddings = []\n    for i in range(0, len(texts), 256):\n        batch = texts[i:i+256]\n        resp = embedding(model=self.embedding_model, input=batch)\n        embeddings.extend([item[\"embedding\"] for item in resp[\"data\"]])\n\n    X = np.asarray(embeddings, dtype=\"float32\")\n    faiss.normalize_L2(X)\n\n    np.save(f\"{output_dir}/embeddings_part_{partition_id}.npy\", X)\n    np.save(f\"{output_dir}/index_part_{partition_id}.npy\", row_index)\n```\n\n资料来源：[datatune/core/deduplication.py:70-130]()\n\n**FAISS索引构建：**\n\n```python\ndef build_faiss_index(self, embedding_dir, dim, hnsw_m, ef_search):\n    index = faiss.IndexHNSWFlat(\n        dim,\n        hnsw_m,\n        faiss.METRIC_INNER_PRODUCT,\n    )\n    index.hnsw.efConstruction = 200\n```\n\n资料来源：[datatune/core/deduplication.py:140-160]()\n\n### 构造器参数\n\n| 参数 | 类型 | 默认值 | 说明 |\n|------|------|--------|------|\n| `llm` | `LLM` | 无 | 用于执行去重的语言模型实例 |\n| `embedding_model` | `str` | `\"text-embedding-3-small\"` | 嵌入模型名称 |\n| `sim_threshold` | `float` | `0.90` | 相似度阈值，超过此值的记录被视为重复 |\n| `top_k` | `int` | `50` | 每个记录查找的最大相似记录数 |\n| `hnsw_m` | `int` | `32` | HNSW索引的空间参数M |\n| `ef_search` | `int` | `64` | HNSW搜索时的搜索参数ef |\n| `return_df` | `bool` | `False` | 是否返回DataFrame格式结果 |\n\n资料来源：[datatune/core/deduplication.py:160-180]()\n\n## 自定义归约动作\n\n用户可以通过`@register_action`装饰器注册自定义归约动作，扩展reduce模块的功能。\n\n### 创建自定义动作示例\n\n```python\nfrom datatune.core.reduce import register_action\n\n@register_action(\"group_stats\")\nclass GroupStatsReducer:\n    def __init__(self, group_by_column, stat_columns):\n        self.group_by_column = group_by_column\n        self.stat_columns = stat_columns\n    \n    def __call__(self, df):\n        return df.groupby(self.group_by_column)[self.stat_columns].agg(['mean', 'sum', 'count'])\n```\n\n### 调用自定义动作\n\n```python\nimport datatune as dt\n\nresult = dt.reduce(df, action=\"group_stats\", group_by_column=\"category\", stat_columns=[\"price\", \"quantity\"])\n```\n\n## 执行流程\n\n```mermaid\nsequenceDiagram\n    participant User as 用户\n    participant Reduce as reduce函数\n    participant Registry as 动作注册表\n    participant Action as 动作类\n    participant Result as 处理结果\n\n    User->>Reduce: reduce(df, action=\"xxx\", **kwargs)\n    Reduce->>Registry: get_action(\"xxx\")\n    Registry-->>Reduce: 动作类引用\n    Reduce->>Action: 实例化 cls(**kwargs)\n    Reduce->>Action: reducer(df)\n    Action->>Result: 执行归约逻辑\n    Result-->>User: 返回处理结果\n```\n\n## 与Map和Filter的协作\n\n在datatune的整体架构中，Reduce操作通常与其他两种操作配合使用。Map操作用于生成新的列字段，Filter操作用于数据筛选，Reduce操作用于聚合和去重。\n\n```mermaid\ngraph LR\n    A[原始数据] --> B[Map操作]\n    B --> C[新增列数据]\n    C --> D[Filter操作]\n    D --> E[筛选后数据]\n    E --> F[Reduce操作]\n    F --> G[聚合/去重结果]\n    G --> H[finalize]\n    H --> I[计算结果]\n```\n\n### 工作流程示例\n\n```python\nimport datatune as dt\nfrom datatune.llm.llm import OpenAI\nimport dask.dataframe as dd\n\nllm = OpenAI(model_name=\"gpt-3.5-turbo\")\ndf = dd.read_csv(\"products.csv\")\n\n# Step 1: 使用Map提取类别信息\nmapped = dt.map(\n    prompt=\"从产品描述中提取主要类别\",\n    output_fields=[\"Category\"],\n    input_fields=[\"Description\", \"Name\"]\n)(llm, df)\n\n# Step 2: 使用Filter筛选特定类别\nfiltered = dt.filter(\n    prompt=\"只保留电子产品\",\n    input_fields=[\"Category\"]\n)(llm, mapped)\n\n# Step 3: 使用Reduce进行语义去重\ndeduplicated = dt.reduce(\n    df=dt.finalize(filtered),\n    action=\"deduplicate\",\n    embedding_model=\"text-embedding-3-small\"\n)\n\n# Step 4: 最终计算输出\nresult = deduplicated.compute()\n```\n\n资料来源：[datatune/__init__.py:1-10]()\n\n## 注意事项\n\n1. **动作注册时机**：自定义动作需要在调用`reduce`函数之前完成注册，确保动作类已在`_ACTIONS`注册表中可用。\n\n2. **参数传递**：`reduce`函数使用关键字参数`**kwargs`将配置传递给动作类构造器，调用者需确保参数名与动作类构造器参数匹配。\n\n3. **异常处理**：当指定的动作名称不存在时，`get_action`函数会抛出`ValueError`异常。应用程序应做好异常捕获处理。\n\n4. **Dask集成**：Reduce操作基于Dask DataFrame实现，支持大规模数据的分布式处理。输入数据应为`dask.dataframe.DataFrame`类型。资料来源：[datatune/core/reduce.py:1]()\n\n5. **分区处理**：在处理大规模数据时，系统会按分区逐个处理，用户应合理配置Dask集群或本地并行参数以优化性能。\n\n---\n\n<a id='agent-system'></a>\n\n## Agent系统\n\n### 相关页面\n\n相关主题：[LLM集成](#llm-integration), [Map操作](#map-operation), [Filter操作](#filter-operation)\n\n<details>\n<summary>相关源码文件</summary>\n\n以下源码文件用于生成本页说明：\n\n- [datatune/agent/agent.py](https://github.com/vitalops/datatune/blob/main/datatune/agent/agent.py)\n- [datatune/agent/__init__.py](https://github.com/vitalops/datatune/blob/main/datatune/agent/__init__.py)\n- [datatune/llm/llm.py](https://github.com/vitalops/datatune/blob/main/datatune/llm/llm.py)\n- [datatune/__init__.py](https://github.com/vitalops/datatune/blob/main/datatune/__init__.py)\n- [datatune/core/dask/filter_dask.py](https://github.com/vitalops/datatune/blob/main/datatune/core/dask/filter_dask.py)\n- [datatune/core/dask/map_dask.py](https://github.com/vitalops/datatune/blob/main/datatune/core/dask/map_dask.py)\n</details>\n\n# Agent系统\n\n## 概述\n\nAgent系统是Datatune的核心智能组件，它允许用户使用自然语言描述数据处理目标，系统自动解析用户意图，生成并执行数据转换计划。该系统结合了大型语言模型（LLM）的推理能力与Dask分布式计算框架，能够处理复杂的多步骤数据转换任务，无需用户手动编写代码。\n\nAgent的核心设计理念是：**用户描述期望的结果，系统自动决定使用何种操作以及如何组合这些操作**。这使得非技术用户也能完成复杂的数据处理任务，同时保持足够的灵活性供高级用户使用。\n\n资料来源：[datatune/agent/__init__.py:1-30]()\n\n## 架构设计\n\n### 系统组件\n\nAgent系统由以下核心组件构成：\n\n| 组件 | 文件位置 | 职责 |\n|------|---------|------|\n| Agent | `datatune/agent/agent.py` | 主要执行引擎，负责计划生成与执行 |\n| Agent基类 | `datatune/agent/__init__.py` | 定义系统提示词和抽象接口 |\n| LLM | `datatune/llm/llm.py` | 提供多后端LLM调用能力 |\n| Map原语 | `datatune/core/dask/map_dask.py` | 基于LLM的列映射转换 |\n| Filter原语 | `datatune/core/dask/filter_dask.py` | 基于LLM的行过滤 |\n\n### 数据流架构\n\n```mermaid\ngraph TD\n    A[用户自然语言目标] --> B[Agent.do方法]\n    B --> C[LLM生成计划JSON]\n    C --> D[计划验证]\n    D --> E{计划类型}\n    E -->|dask| F[Dask操作模板]\n    E -->|primitive| G[Map/Filter原语]\n    F --> H[代码执行引擎]\n    G --> H\n    H --> I[错误处理]\n    I -->|成功| J[最终结果]\n    I -->|失败| K[错误恢复]\n    K --> C\n```\n\nAgent接收用户的自然语言指令后，首先调用LLM生成结构化的执行计划。计划由一系列步骤组成，每个步骤可以是Dask原生操作或LLM驱动的原语操作。\n\n资料来源：[datatune/agent/agent.py:1-50]()\n\n## 核心类设计\n\n### Agent类\n\n`Agent`是系统的主要执行类，封装了所有数据处理逻辑。\n\n```python\nclass Agent:\n    def __init__(self, llm: LLM, verbose: bool = False):\n        self.llm = llm\n        self.history: List[Dict[str, Any]] = []\n        self.verbose = verbose\n```\n\n**初始化参数：**\n\n| 参数 | 类型 | 必填 | 说明 |\n|------|------|------|------|\n| `llm` | `LLM` | 是 | LLM实例，用于生成计划和执行原语 |\n| `verbose` | `bool` | 否 | 设为True时启用DEBUG级别日志，默认False |\n\n**主要方法：**\n\n| 方法 | 返回值 | 说明 |\n|------|--------|------|\n| `do(goal: str, df: dd.DataFrame)` | `None \\| str` | 执行自然语言目标 |\n| `_generate_plan(goal)` | `List[Dict]` | 生成执行计划 |\n| `_execute_plan(plan)` | `Tuple[None, int] \\| Tuple[str, int]` | 执行完整计划 |\n| `_execute_step(step)` | `None \\| str` | 执行单个步骤 |\n| `log_primitive(df, message)` | `dd.DataFrame` | 记录原语执行状态 |\n\n资料来源：[datatune/agent/agent.py:50-80]()\n\n### Agent基类\n\n```python\nclass Agent(ABC):\n    system_prompt: str = \"\"\"You are Datatune Agent, a powerful assistant designed to help users with data processing tasks.\n    You are capable of generating python code to perform various operations on data. Apart from python builtins, you have the following libraries avaiable in your run time:\n    - pandas\n    - numpy\n    - dask\n\n    In addition to these, you also have access to the datatune libarary, which provides functionality for processing data using LLMs.\n    ...\"\"\"\n```\n\n基类定义了Agent的系统提示词，明确了可用的运行时环境：Python内置库、pandas、numpy、dask以及datatune库。\n\n资料来源：[datatune/agent/__init__.py:1-30]()\n\n## 执行计划模型\n\n### 计划结构\n\n执行计划是一个JSON数组，每个元素代表一个独立的执行步骤：\n\n```json\n[\n  {\n    \"type\": \"dask\" | \"primitive\",\n    \"operation\": \"操作名称\",\n    \"params\": { ... },\n    \"subprompt\": \"LLM提示词\",\n    \"input_fields\": [\"列名\"],\n    \"output_fields\": [\"新列名\"]\n  }\n]\n```\n\n### 步骤类型\n\nAgent支持两种类型的操作步骤：\n\n| 类型 | 说明 | 示例操作 |\n|------|------|----------|\n| `dask` | 直接的Dask DataFrame操作 | `add_column`, `group_by`, `rename_columns` |\n| `primitive` | LLM驱动的原语操作 | `Map`, `Filter` |\n\n### Dask操作模板\n\n系统预定义了丰富的Dask操作模板：\n\n| 操作名称 | 功能描述 | 关键参数 |\n|----------|----------|----------|\n| `add_column` | 添加新列（基于表达式） | `column`, `expr` |\n| `apply_function` | 对列应用函数 | `column`, `func` |\n| `rename_columns` | 重命名列 | `mapping` |\n| `astype_column` | 更改列数据类型 | `column`, `dtype` |\n\n资料来源：[datatune/agent/agent.py:80-150]()\n\n## 原语系统\n\n### Map原语\n\n`Map`原语使用LLM根据自然语言提示词从现有列生成新列。它能够处理语义提取、分类、解释等复杂转换任务。\n\n**工作流程：**\n\n1. 序列化输入列数据为字典格式\n2. 将数据分批发送给LLM\n3. LLM返回包含新字段的字典\n4. 将结果合并回DataFrame\n\n```python\nmap = dt.Map(prompt=\"Extract categories from the description\")\nmapped_df = map(llm, df)\n```\n\n资料来源：[datatune/core/dask/map_dask.py:1-60]()\n\n### Filter原语\n\n`Filter`原语使用LLM根据自然语言条件过滤DataFrame行。\n\n**输出格式规范：**\n\n```\nindex=<row_index>|{...字典内容..., '__filter__': True|False}<endofrow>\n```\n\n- `True`：保留该行\n- `False`：移除该行\n\n资料来源：[datatune/core/dask/filter_dask.py:1-50]()\n\n## LLM后端支持\n\n系统通过统一的`LLM`接口支持多种语言模型提供商：\n\n```mermaid\ngraph LR\n    A[LLM基类] --> B[OpenAI]\n    A --> C[Ollama]\n    A --> D[VLLM]\n    A --> E[Azure]\n```\n\n### 支持的模型\n\n| 提供商 | 模型标识 | 默认模型 |\n|--------|----------|----------|\n| OpenAI | `openai/<model>` | `gpt-3.5-turbo` |\n| Ollama | `ollama_chat/<model>` | `gemma3:4b` |\n| VLLM | `openai/<model>` | - |\n| Azure | `azure/<model>` | - |\n\n### 速率限制\n\n系统内置了主流模型的速率限制配置：\n\n| 模型 | TPM (每分钟Token数) | RPM (每分钟请求数) |\n|------|---------------------|-------------------|\n| `gpt-3.5-turbo` | 200,000 | 500 |\n| `gpt-4` | 10,000 | 500 |\n| `gpt-4-turbo` | 30,000 | 500 |\n| `gpt-4o` | 30,000 | 500 |\n\n资料来源：[datatune/llm/model_rate_limits.py:1-80]()\n\n## 使用示例\n\n### 基础用法\n\n```python\nimport datatune as dt\nfrom datatune.llm.llm import OpenAI\nimport dask.dataframe as dd\n\n# 初始化LLM和Agent\nllm = OpenAI(model_name=\"gpt-3.5-turbo\")\nagent = dt.Agent(llm)\n\n# 加载数据\ndf = dd.read_csv(\"products.csv\")\n\n# 使用自然语言描述任务\ndf = agent.do(\"Add ProfitMargin column and keep only African organizations\", df)\nresult = dt.finalize(df)\n```\n\n### 详细示例\n\n```python\n# 创建Agent实例（启用详细日志）\nagent = dt.Agent(llm, verbose=True)\n\n# 执行复杂的多步骤任务\ngoal = \"\"\"\n1. Create a 'PriceCategory' column: 'high' if price > 100, 'low' otherwise\n2. Keep only rows where PriceCategory is 'high'\n3. Add 'DiscountedPrice' column: Price * 0.9\n\"\"\"\n\ndf = agent.do(goal, df)\nresult = dt.finalize(df)\n```\n\n### 单独使用Map和Filter\n\n```python\n# 仅使用Map原语\nmapped = dt.map(\n    prompt=\"Extract categories from the description and name\",\n    output_fields=[\"Category\", \"Subcategory\"],\n    input_fields=[\"Description\", \"Name\"]\n)(llm, df)\n\n# 仅使用Filter原语\nfiltered = dt.filter(\n    prompt=\"Keep only electronics products\",\n    input_fields=[\"Name\"]\n)(llm, mapped)\n```\n\n## 执行流程详解\n\n### 计划生成\n\n```mermaid\nsequenceDiagram\n    participant User as 用户\n    participant Agent as Agent\n    participant LLM as LLM\n    participant Schema as 数据Schema\n\n    User->>Agent: do(goal, df)\n    Agent->>Schema: 获取DataFrame结构\n    Agent->>LLM: 发送persona_prompt + schema_prompt\n    LLM->>Agent: 返回JSON计划\n    Agent->>Agent: 验证计划格式\n```\n\nAgent生成计划时，会向LLM传递：\n1. **Persona提示词**：定义LLM的角色、能力边界和输出格式\n2. **Schema提示词**：DataFrame的列结构和数据类型\n3. **用户目标**：自然语言描述的期望结果\n\n### 计划执行\n\n执行过程采用流式日志记录：\n\n```python\n# 每个步骤的执行流程\ndf = df.map_partitions(log_primitive, \"🔄 开始步骤 X/Y\", meta=df._meta)\nstep_num += 1\n# 执行具体操作\ndf = df.map_partitions(log_primitive, \"📍 完成步骤 X/Y\", meta=df._meta)\n```\n\n最终执行完成后，调用`dt.finalize()`和`compute()`将Dask DataFrame转换为实际的Pandas DataFrame。\n\n资料来源：[datatune/agent/agent.py:100-140]()\n\n## 错误处理与恢复\n\n### 错误检测机制\n\n系统在每个步骤执行后都会检测异常：\n\n```python\ntry:\n    # 执行步骤\n    ...\nexcept Exception as e:\n    error_msg = f\"{type(e).__name__}: {str(e)}\\n{traceback.format_exc()}\"\n    return error_msg\n```\n\n### 错误恢复流程\n\n当步骤执行失败时，系统会：\n1. 记录错误信息和失败的步骤详情\n2. 生成包含错误上下文的修复提示词\n3. 请求LLM生成修正后的计划\n\n```python\ndef get_error_prompt(self, error_msg: str, failed_step: Dict) -> str:\n    error_prompt = f\"\"\"\n    The previous code execution failed with the following error:\n    Error: {error_msg}\n    Failed step: {failed_step}\n    Provide the json plan with the corrected step.\n    \"\"\"\n```\n\n## 配置与调优\n\n### 初始化配置\n\n```python\n# 完整配置示例\nagent = dt.Agent(\n    llm=llm,\n    verbose=True  # 启用DEBUG日志\n)\n\n# 自定义LLM配置\nllm = OpenAI(\n    model_name=\"gpt-4-turbo\",\n    api_key=\"your-api-key\",\n    rpm=1000,  # 自定义RPM限制\n    tpm=50000  # 自定义TPM限制\n)\n```\n\n### 日志级别控制\n\n```python\nimport logging\n\n# 启用详细日志\nagent = dt.Agent(llm, verbose=True)\nlogger.setLevel(logging.DEBUG)\n\n# 禁用详细日志\nagent = dt.Agent(llm, verbose=False)\nlogger.setLevel(logging.INFO)\n```\n\n## 最佳实践\n\n1. **明确指定列名**：在目标描述中明确提及要操作的列名，有助于Agent生成准确的操作计划\n\n2. **分解复杂任务**：对于非常复杂的任务，可以考虑分步执行以便于调试\n\n3. **选择合适的模型**：简单的数据转换可使用`gpt-3.5-turbo`，复杂的语义任务建议使用`gpt-4-turbo`或`gpt-4o`\n\n4. **监控执行过程**：通过设置`verbose=True`监控Agent的决策过程，便于理解系统行为\n\n5. **处理重复数据**：系统内置了基于语义相似度的去重功能，可用于处理LLM输出中的重复结果\n\n---\n\n<a id='llm-integration'></a>\n\n## LLM集成\n\n### 相关页面\n\n相关主题：[Agent系统](#agent-system)\n\n<details>\n<summary>相关源码文件</summary>\n\n以下源码文件用于生成本页说明：\n\n- [datatune/llm/llm.py](https://github.com/vitalops/datatune/blob/main/datatune/llm/llm.py)\n- [datatune/llm/model_rate_limits.py](https://github.com/vitalops/datatuple/blob/main/datatune/llm/model_rate_limits.py)\n</details>\n\n# LLM集成\n\n## 概述\n\ndatatune 的 LLM 集成模块提供了统一的接口来连接各种大语言模型（Large Language Model）提供者。该模块封装了不同 LLM 服务商的 API 调用逻辑，使上层应用能够以一致的方式使用不同的模型，而无需关心底层实现的差异。\n\n主要功能包括：\n\n- **统一抽象**：通过基类 `LLM` 提供一致的接口设计\n- **多提供商支持**：支持 OpenAI、Ollama、VLLM、Azure、Mistral、Huggingface 等主流 LLM 服务\n- **速率限制管理**：内置对各模型速率限制的支持，防止 API 调用超出限制\n- **Token 计数与批处理**：提供 token 计数和批量请求功能，优化 API 使用效率\n\n## 架构设计\n\n### 类继承结构\n\n```mermaid\ngraph TD\n    LLMBase[LLM 基类] --> Ollama\n    LLMBase --> OpenAI\n    LLMBase --> VLLM\n    LLMBase --> Azure\n    LLMBase --> Gemini\n    LLMBase --> Mistral\n    LLMBase --> Huggingface\n    \n    LLMBase --> 完成方法[_completion]\n    LLMBase --> 批处理方法[_create_batched_prompts]\n    LLMBase --> Token限制[MAX_RPM, MAX_TPM]\n```\n\n### 核心类说明\n\n#### LLM 基类\n\n`LLM` 类是所有 LLM 提供者的基类，定义通用接口和默认行为：\n\n```python\nclass LLM:\n    def __init__(self, model_name: str, **kwargs) -> None:\n        self.model_name = model_name  # 格式: \"provider/model\"\n        self._base_model_name = model_name.split(\"/\", 1)[1]  # 如 \"gpt-3.5-turbo\"\n```\n\n**资料来源**：[datatune/llm/llm.py:27-32]()\n\n#### 速率限制\n\n每个模型实例具有两个关键属性：\n\n| 属性 | 说明 | 来源 |\n|------|------|------|\n| `MAX_RPM` | 每分钟最大请求数 (Requests Per Minute) | model_rate_limits |\n| `MAX_TPM` | 每分钟最大 Token 数 (Tokens Per Minute) | model_rate_limits |\n| `max_tokens` | 单次请求最大 Token 数 | `get_max_tokens()` |\n\n**资料来源**：[datatune/llm/llm.py:62-64]()\n\n## 支持的 LLM 提供者\n\n### 1. OpenAI\n\nOpenAI 模型使用 `openai/` 前缀标识：\n\n```python\nfrom datatune.llm.llm import OpenAI\n\nllm = OpenAI(model_name=\"gpt-3.5-turbo\", api_key=\"your-key\")\nllm = OpenAI(model_name=\"gpt-4o\", api_key=\"your-key\")\n```\n\n**资料来源**：[datatune/llm/llm.py:64-73]()\n\n### 2. Ollama (本地部署)\n\n适用于本地运行的 LLM，通过 HTTP API 连接：\n\n```python\nfrom datatune.llm.llm import Ollama\n\nllm = Ollama()  # 默认 gemma3:4b，localhost:11434\nllm = Ollama(model_name=\"llama3:8b\", api_base=\"http://localhost:11434\")\n```\n\n**资料来源**：[datatune/llm/llm.py:53-61]()\n\n### 3. VLLM\n\nVLLM 服务器连接，支持 OpenAI 兼容 API：\n\n```python\nfrom datatune.llm.llm import VLLM\n\nllm = VLLM(\n    model_name=\"meta-llama/Llama-3-8B-Instruct\",\n    api_base=\"http://localhost:8000/v1\"\n)\n```\n\n**资料来源**：[datatune/llm/llm.py:75-91]()\n\n### 4. Azure OpenAI\n\nAzure 托管的 OpenAI 模型：\n\n```python\nfrom datatune.llm.llm import Azure\n\nllm = Azure(\n    model_name=\"gpt-3.5-turbo\",\n    api_key=\"your-key\",\n    api_base=\"https://xxx.openai.azure.com\",\n    api_version=\"2024-02-01\"\n)\n```\n\n**资料来源**：[datatune/llm/llm.py:93-106]()\n\n### 5. Gemini\n\nGoogle Gemini 模型集成：\n\n```python\nfrom datatune.llm.llm import Gemini\n\nllm = Gemini(model_name=\"gemini-1.5-pro\", api_key=\"your-key\")\n```\n\n### 6. Mistral\n\nMistral AI 模型：\n\n```python\nfrom datatune.llm.llm import Mistral\n\nllm = Mistral(model_name=\"mistral-tiny\")\nllm = Mistral(model_name=\"mistral-large-latest\")\n```\n\n**资料来源**：[datatune/llm/llm.py:130-148]()\n\n### 7. Huggingface\n\nHugging Face 推理端点：\n\n```python\nfrom datatune.llm.llm import Huggingface\n\nllm = Huggingface(model_name=\"meta-llama/Llama-3-8B-Instruct\")\n```\n\n**资料来源**：[datatune/llm/llm.py:150-172]()\n\n## 速率限制配置\n\n### 预定义模型限制\n\n`model_rate_limits.py` 文件包含各模型的默认速率限制：\n\n| 模型系列 | TPM (Token/分钟) | RPM (请求/分钟) |\n|----------|------------------|-----------------|\n| GPT-3.5-Turbo | 200,000 | 500 |\n| GPT-4 | 10,000 - 30,000 | 500 |\n| GPT-4.1 | 30,000 - 200,000 | 500 |\n| GPT-4o | 30,000 | 500 |\n| GPT-4.5-Preview | 125,000 | 1,000 |\n\n**资料来源**：[datatune/llm/model_rate_limits.py:1-85]()\n\n### 自定义速率限制\n\n可以在初始化时覆盖默认限制：\n\n```python\nfrom datatune.llm.llm import OpenAI\n\n# 自定义 RPM 和 TPM\nllm = OpenAI(\n    model_name=\"gpt-3.5-turbo\",\n    rpm=1000,\n    tpm=300000\n)\n```\n\n**资料来源**：[datatune/llm/llm.py:42-51]()\n\n## 使用方式\n\n### 基本调用\n\n所有 LLM 实例可作为可调用对象使用：\n\n```python\nllm = OpenAI(model_name=\"gpt-3.5-turbo\", api_key=\"sk-xxx\")\n\n# 单次调用\nresult = llm(\"Hello, how are you?\")\n```\n\n### 批处理调用\n\n对于大量数据处理，系统支持自动批处理：\n\n```python\nprompts = [\"Process row 1\", \"Process row 2\", \"Process row 3\"]\nresults = llm(prompts, prefix=\"Instruction: \", suffix=\"End.\")\n```\n\n**资料来源**：[datatune/llm/llm.py:85-105]()\n\n### 在 datatune 中的应用\n\ndatatune 的核心功能使用 LLM 集成进行数据转换：\n\n```python\nimport datatune as dt\nfrom datatune.llm.llm import OpenAI\n\nllm = OpenAI(model_name=\"gpt-3.5-turbo\")\n\n# 映射操作\nmapped = dt.map(\n    prompt=\"Extract categories from description\",\n    output_fields=[\"Category\"],\n    input_fields=[\"Description\"]\n)(llm, df)\n\n# 过滤操作\nfiltered = dt.filter(\n    prompt=\"Keep only electronics products\",\n    input_fields=[\"Name\"]\n)(llm, mapped)\n```\n\n**资料来源**：[README.md](https://github.com/vitalops/datatune/blob/main/README.md)\n\n## 核心方法\n\n### `_completion`\n\n执行单次补全请求：\n\n```python\ndef _completion(self, prompt: str) -> Union[str, Exception]:\n    messages = [{\"role\": \"user\", \"content\": prompt}]\n    from litellm import completion\n    \n    response = completion(model=self.model_name, messages=messages, **self.kwargs)\n    \n    if isinstance(response, Exception):\n        return response\n    return response[\"choices\"][0][\"message\"][\"content\"]\n```\n\n**资料来源**：[datatune/llm/llm.py:69-77]()\n\n### `get_max_tokens`\n\n获取模型支持的最大 token 数：\n\n```python\ndef get_max_tokens(self) -> int:\n    return get_max_tokens(self._base_model_name)\n```\n\n### `_create_batched_prompts`\n\n创建批处理提示词，用于优化多行数据处理：\n\n```python\ndef _create_batched_prompts(\n    self,\n    input_rows: List[str],\n    user_batch_prefix: str,\n    prompt_per_row: str,\n    batch_suffix: str = None\n) -> List[str]:\n    # 计算 token 限制并分批\n    # 返回批处理后的提示词列表\n```\n\n**资料来源**：[datatune/llm/llm.py:80-107]()\n\n## 依赖说明\n\nLLM 模块依赖 `litellm` 库实现底层 API 调用：\n\n```python\nfrom litellm import get_max_tokens, token_counter, batch_completion\n```\n\nlitellm 提供了对 100+ LLM 模型的统一接口，支持：\n- OpenAI 系列\n- Azure OpenAI\n- Anthropic\n- Google Vertex AI\n- AWS Bedrock\n- 本地部署模型 (Ollama, VLLM, TGI)\n- Hugging Face 推理端点\n\n**资料来源**：[datatune/llm/llm.py:10-12]()\n\n## 最佳实践\n\n### 1. 选择合适的模型\n\n| 场景 | 推荐模型 | 理由 |\n|------|----------|------|\n| 快速原型 | gpt-3.5-turbo | 成本低、速度快 |\n| 高质量输出 | gpt-4o | 性能与成本平衡 |\n| 最大精度 | gpt-4.1 | 最新最强模型 |\n| 本地部署 | Ollama/VLLM | 隐私保护、无 API 成本 |\n\n### 2. 配置合理的速率限制\n\n根据实际 API 配额设置：\n\n```python\n# 根据 API tier 设置\nllm = OpenAI(\n    model_name=\"gpt-4o\",\n    rpm=500,   # 根据配额调整\n    tpm=30000  # 根据配额调整\n)\n```\n\n### 3. 使用批处理优化\n\n对于大量数据处理，使用内置批处理功能避免单次调用限制：\n\n```python\n# 自动分批处理\nresults = llm(\n    input_rows,\n    prefix=\"任务: \",\n    suffix=\"请以指定格式输出\",\n    optimized=True  # 启用优化模式\n)\n```\n\n## 导出接口\n\n模块入口文件提供统一导出：\n\n```python\nfrom datatune.llm import *\n# 导出: LLM, Ollama, OpenAI, VLLM, Azure, Gemini, Mistral, Huggingface\n```\n\n**资料来源**：[datatune/llm/__init__.py]()\n\n## 总结\n\ndatatune 的 LLM 集成模块通过抽象化的设计，为用户提供了便捷的模型切换能力和统一的调用接口。开发者可以轻松地在不同 LLM 提供者之间切换，同时享受内置的速率限制保护和批处理优化功能。\n\n---\n\n<a id='datasource'></a>\n\n## 数据源支持\n\n### 相关页面\n\n相关主题：[系统架构](#architecture), [Map操作](#map-operation), [Filter操作](#filter-operation)\n\n<details>\n<summary>相关源码文件</summary>\n\n以下源码文件用于生成本页说明：\n\n- [datatune/llm/llm.py](https://github.com/vitalops/datatune/blob/main/datatune/llm/llm.py)\n- [datatune/agent/agent.py](https://github.com/vitalops/datatune/blob/main/datatune/agent/agent.py)\n- [datatune/core/dask/filter_dask.py](https://github.com/vitalops/datatune/blob/main/datatune/core/dask/filter_dask.py)\n- [datatune/core/dask/map_dask.py](https://github.com/vitalops/datatune/blob/main/datatune/core/dask/map_dask.py)\n- [datatune/core/ibis/filter_ibis.py](https://github.com/vitalops/datatune/blob/main/datatune/core/ibis/filter_ibis.py)\n- [datatune/core/ibis/map_ibis.py](https://github.com/vitalops/datatune/blob/main/datatune/core/ibis/map_ibis.py)\n- [datatune/agent/__init__.py](https://github.com/vitalops/datatune/blob/main/datatune/agent/__init__.py)\n</details>\n\n# 数据源支持\n\n## 概述\n\nDatatune 是一个数据处理框架，通过集成大语言模型（LLM）来实现智能化的数据转换和清洗。该框架的核心设计理念是支持多种数据源，使得用户能够在不同的计算后端上执行 `map`、`filter` 等数据转换操作。\n\n数据源支持架构采用插件式设计，通过抽象层将具体的数据处理逻辑与底层数据源解耦。当前框架主要支持两大类数据源：**Dask**（用于大规模并行计算）和 **Ibis**（支持多种 SQL 后端）。\n\n资料来源：[datatune/agent/__init__.py:1-5]()\n\n## 支持的数据源类型\n\nDatatune 支持的数据源可分为以下几类：\n\n| 数据源类型 | 底层引擎 | 特点 | 适用场景 |\n|-----------|---------|------|---------|\n| Dask DataFrame | Dask | 延迟计算、分布式执行、内存优化 | 大规模单机或集群数据处理 |\n| DuckDB | Ibis + DuckDB | 嵌入式 OLAP、零配置 | 快速分析、交互式查询 |\n| PostgreSQL | Ibis + PostgreSQL | 关系型数据库、企业级支持 | 生产环境、事务处理 |\n| BigQuery | Ibis + BigQuery | 云原生、超大规模 | 云端大数据分析 |\n\n资料来源：[README.md:1-50]()\n\n## 架构设计\n\n### 核心抽象层\n\nDatatune 的数据源支持通过统一的抽象接口实现，主要包含以下核心组件：\n\n```python\n# 抽象基类定义\nclass Agent(ABC):\n    system_prompt: str = \"\"\"You are Datatune Agent...\"\"\"\n```\n\n代理（Agent）类作为核心抽象，负责协调 LLM 与数据源的交互。不同的数据源实现继承该基类并实现具体的数据操作逻辑。\n\n资料来源：[datatune/agent/__init__.py:1-10]()\n\n### 数据流架构\n\n```mermaid\ngraph TD\n    A[用户代码] --> B[Datatune API]\n    B --> C{数据源类型}\n    C -->|Dask| D[core/dask/]\n    C -->|Ibis| E[core/ibis/]\n    D --> F[map_dask.py / filter_dask.py]\n    E --> G[map_ibis.py / filter_ibis.py]\n    F --> H[LLM 调用]\n    G --> H\n    H --> I[结果聚合]\n    I --> J[compute / execute]\n```\n\n## Dask 数据源\n\n### 概述\n\nDask 是 Datatune 默认支持的数据源，提供大规模并行计算能力。通过 Dask DataFrame，用户可以处理超出内存限制的数据集。\n\n### 主要操作\n\nDask 数据源支持两类核心操作：\n\n#### Map 操作\n\n用于从现有列生成新列，常用于语义提取、分类、自然语言推理等场景。\n\n```python\nimport datatune as dt\nimport dask.dataframe as dd\n\ndf = dd.read_csv(\"products.csv\")\n\nmapped = dt.map(\n    prompt=\"Extract categories from the description and name of product.\",\n    output_fields=[\"Category\", \"Subcategory\"],\n    input_fields=[\"Description\", \"Name\"]\n)(llm, df)\n```\n\n资料来源：[README.md:1-30]()\n\n#### Filter 操作\n\n用于根据条件过滤行，基于 LLM 理解保留符合条件的记录。\n\n```python\nfiltered = dt.filter(\n    prompt=\"Keep only electronics products\",\n    input_fields=[\"Name\"]\n)(llm, mapped)\n```\n\n资料来源：[README.md:1-35]()\n\n### Dask 实现细节\n\n#### 输出格式规范\n\nMap 和 Filter 操作要求 LLM 返回特定格式的输出：\n\n```\nindex=<row_index>|{key1: value1, key2: value2, ...}<endofrow>\n```\n\n- 字符串必须用双引号包裹\n- 缺失值设置为 `None` 或 `null`\n- 每行输出必须以 `<endofrow>` 结尾\n\n资料来源：[datatune/core/dask/map_dask.py:1-30]()\n\n#### 重复数据处理\n\nDask 实现包含智能去重机制，通过 `dup_to_canon` 映射处理重复数据：\n\n```python\ndup_to_canon = {\n    dup: c[\"canonical_id\"]\n    for c in clusters\n    for dup in c[\"duplicate_ids\"]\n}\n```\n\n只有规范索引的数据会被发送到 LLM，重复项的结果会自动复制。\n\n资料来源：[datatune/core/dask/map_dask.py:1-45]()\n\n## Ibis 数据源\n\n### 概述\n\nIbis 是一个 Python 抽象层，支持多种 SQL 后端。通过 Ibis，Datatune 可以连接到 DuckDB、PostgreSQL、BigQuery 等数据库执行向量化查询。\n\n### 连接方式\n\n#### DuckDB\n\n```python\nimport ibis\ncon = ibis.duckdb.connect(\"data.duckdb\")\ntable = con.table(\"my_table\")\n```\n\n资料来源：[README.md:1-60]()\n\n#### PostgreSQL\n\n```python\nimport ibis\ncon = ibis.postgres.connect(...)\ntable = con.table(\"my_table\")\n```\n\n#### BigQuery\n\n```python\nimport ibis\ncon = ibis.bigquery.connect(...)\ntable = con.table(\"my_table\")\n```\n\n### Ibis 实现特点\n\n#### 行号索引\n\nIbis 实现使用 `ibis.row_number()` 生成行索引：\n\n```python\nindexed_table = table.mutate(_ROW_ID_=ibis.row_number().cast(\"int64\"))\nlocal_data = indexed_table.select(\"_ROW_ID_\", input_col).execute()\n```\n\n资料来源：[datatune/core/ibis/filter_ibis.py:1-40]()\n\n#### 输出解析\n\nIbis 后端将 LLM 输出解析为布尔值用于过滤：\n\n```python\nfor res in results:\n    try:\n        d = ast.literal_eval(str(res).strip())\n        if d:\n            last_key = list(d.keys())[-1]\n            flag = d[last_key]\n            out_bools.append(flag)\n    except Exception:\n        out_bools.append(False)\n```\n\n资料来源：[datatune/core/ibis/filter_ibis.py:1-60]()\n\n## Agent 自动化\n\n### 功能概述\n\nDatatune Agent 可以自动识别数据转换需求，智能选择合适的操作类型并执行。\n\n```python\nimport datatune as dt\nfrom datatune.llm.llm import OpenAI\n\nllm = OpenAI(model_name=\"gpt-3.5-turbo\")\nagent = dt.Agent(llm)\n\ndf = agent.do(\"Add ProfitMargin column and keep only African organizations\", df)\n```\n\n资料来源：[README.md:1-45]()\n\n### 计划生成规则\n\nAgent 生成执行计划时遵循以下规则：\n\n1. **Dask 操作选择**：如果任务涉及添加列、分组、移位、按行或按列应用函数，使用 `type: \"dask\"`\n2. **Primitive 操作选择**：如果需要理解自然语言、提取或解释文本内容、进行语义推理，使用 `type: \"primitive\"`\n3. **简化优先**：能用 Dask 完成的步骤优先使用 Dask，只有 Dask 无法实现时才使用 primitives\n\n资料来源：[datatune/agent/agent.py:1-100]()\n\n### 支持的操作类型\n\n| 操作类型 | 说明 | 适用场景 |\n|---------|------|---------|\n| add_column | 从表达式创建新列 | 数值计算、日期提取 |\n| apply_function | 对单列应用函数 | 逐元素转换 |\n| rename_columns | 重命名列 | 列名规范化 |\n| astype_column | 更改列数据类型 | 类型转换 |\n| group_by_agg | 分组聚合 | 统计计算 |\n| conditional_column | 条件列 | 基于条件的新列 |\n\n资料来源：[datatune/agent/agent.py:1-150]()\n\n## LLM 集成\n\n### 支持的模型提供商\n\nDatatune 通过 litellm 库支持多种 LLM 提供商：\n\n| 提供商 | 类名 | 默认模型 | 说明 |\n|-------|------|---------|------|\n| OpenAI | `OpenAI` | gpt-3.5-turbo | OpenAI GPT 系列 |\n| Ollama | `Ollama` | gemma3:4b | 本地模型支持 |\n| vLLM | `VLLM` | 自定义 | 高性能推理 |\n| Azure | `Azure` | 自定义 | Azure OpenAI 服务 |\n\n资料来源：[datatune/llm/llm.py:1-100]()\n\n### 模型初始化示例\n\n```python\n# OpenAI\nfrom datatune.llm.llm import OpenAI\nllm = OpenAI(model_name=\"gpt-3.5-turbo\")\n\n# Ollama (本地)\nfrom datatune.llm.llm import Ollama\nllm = Ollama()\n\n# Azure\nfrom datatune.llm.llm import Azure\nllm = Azure(model_name=\"gpt-3.5-turbo\", api_key=api_key)\n```\n\n资料来源：[README.md:1-70]()\n\n### 速率限制配置\n\n框架内置了主要模型的速率限制配置：\n\n```python\nmodel_rate_limits = {\n    \"gpt-3.5-turbo\": {\n        \"tpm\": 200_000,  # tokens per minute\n        \"rpm\": 500,      # requests per minute\n    },\n    \"gpt-4\": {\n        \"tpm\": 10_000,\n        \"rpm\": 500,\n    },\n    # ...\n}\n```\n\n资料来源：[datatune/llm/model_rate_limits.py:1-50]()\n\n## 使用工作流\n\n### 完整数据处理流程\n\n```mermaid\ngraph LR\n    A[加载数据] --> B[定义 LLM]\n    B --> C[应用 Map]\n    C --> D[应用 Filter]\n    D --> E[执行 Finalize]\n    E --> F[保存结果]\n```\n\n### 代码示例\n\n```python\nimport datatune as dt\nfrom datatune.llm.llm import OpenAI\nimport dask.dataframe as dd\n\n# 初始化\nllm = OpenAI(model_name=\"gpt-3.5-turbo\")\ndf = dd.read_csv(\"products.csv\")\n\n# 映射操作\nmapped = dt.map(\n    prompt=\"Extract categories from the description and name of product.\",\n    output_fields=[\"Category\", \"Subcategory\"],\n    input_fields=[\"Description\", \"Name\"]\n)(llm, df)\n\n# 过滤操作\nfiltered = dt.filter(\n    prompt=\"Keep only electronics products\",\n    input_fields=[\"Name\"]\n)(llm, mapped)\n\n# 最终计算并保存\nresult = dt.finalize(filtered)\nresult.compute().to_csv(\"electronics_products.csv\")\n```\n\n资料来源：[README.md:1-40]()\n\n## 性能优化\n\n### 去重处理\n\n对于包含重复行的数据集，框架采用智能去重策略：\n\n1. 使用 embedding 模型识别语义相似的记录\n2. 将相似记录聚类，只对规范记录调用 LLM\n3. 将结果自动复制到同簇的其他记录\n\n```python\nclass Deduplicator:\n    def __init__(\n        self,\n        embedding_model: str = \"text-embedding-3-small\",\n        sim_threshold: float = 0.90,\n        top_k: int = 50,\n    ):\n        # ...\n```\n\n资料来源：[datatune/core/deduplication.py:1-30]()\n\n### 分区处理\n\nDask 数据源支持分区并行处理：\n\n```python\nfor pid in range(df.npartitions):\n    part = df[column].get_partition(pid)\n    task = dask.delayed(self._embed_and_write_partition)(part, pid, output_dir)\n```\n\n资料来源：[datatune/core/deduplication.py:1-80]()\n\n## 注意事项\n\n1. **数据量控制**：LLM 调用按批次进行，需注意 token 限制\n2. **输出格式**：严格遵循 `index=<index>|{...}<endofrow>` 格式\n3. **字符串转义**：输出必须使用双引号，避免裸文本\n4. **错误处理**：单个记录解析失败不影响整体流程\n5. **资源限制**：生产环境建议配置合理的 `tpm` 和 `rpm` 参数\n\n---\n\n---\n\n## Doramagic 踩坑日志\n\n项目：vitalops/datatune\n\n摘要：发现 7 个潜在踩坑项，其中 0 个为 high/blocking；最高优先级：能力坑 - 能力判断依赖假设。\n\n## 1. 能力坑 · 能力判断依赖假设\n\n- 严重度：medium\n- 证据强度：source_linked\n- 发现：README/documentation is current enough for a first validation pass.\n- 对用户的影响：假设不成立时，用户拿不到承诺的能力。\n- 建议检查：将假设转成下游验证清单。\n- 防护动作：假设必须转成验证项；没有验证结果前不能写成事实。\n- 证据：capability.assumptions | art_fcb598a7a7ed4b2482ca92474f06efe2 | https://github.com/vitalops/datatune#readme | README/documentation is current enough for a first validation pass.\n\n## 2. 维护坑 · 维护活跃度未知\n\n- 严重度：medium\n- 证据强度：source_linked\n- 发现：未记录 last_activity_observed。\n- 对用户的影响：新项目、停更项目和活跃项目会被混在一起，推荐信任度下降。\n- 建议检查：补 GitHub 最近 commit、release、issue/PR 响应信号。\n- 防护动作：维护活跃度未知时，推荐强度不能标为高信任。\n- 证据：evidence.maintainer_signals | art_fcb598a7a7ed4b2482ca92474f06efe2 | https://github.com/vitalops/datatune#readme | last_activity_observed missing\n\n## 3. 安全/权限坑 · 下游验证发现风险项\n\n- 严重度：medium\n- 证据强度：source_linked\n- 发现：no_demo\n- 对用户的影响：下游已经要求复核，不能在页面中弱化。\n- 建议检查：进入安全/权限治理复核队列。\n- 防护动作：下游风险存在时必须保持 review/recommendation 降级。\n- 证据：downstream_validation.risk_items | art_fcb598a7a7ed4b2482ca92474f06efe2 | https://github.com/vitalops/datatune#readme | no_demo; severity=medium\n\n## 4. 安全/权限坑 · 存在安全注意事项\n\n- 严重度：medium\n- 证据强度：source_linked\n- 发现：No sandbox install has been executed yet; downstream must verify before user use.\n- 对用户的影响：用户安装前需要知道权限边界和敏感操作。\n- 建议检查：转成明确权限清单和安全审查提示。\n- 防护动作：安全注意事项必须面向用户前置展示。\n- 证据：risks.safety_notes | art_fcb598a7a7ed4b2482ca92474f06efe2 | https://github.com/vitalops/datatune#readme | No sandbox install has been executed yet; downstream must verify before user use.\n\n## 5. 安全/权限坑 · 存在评分风险\n\n- 严重度：medium\n- 证据强度：source_linked\n- 发现：no_demo\n- 对用户的影响：风险会影响是否适合普通用户安装。\n- 建议检查：把风险写入边界卡，并确认是否需要人工复核。\n- 防护动作：评分风险必须进入边界卡，不能只作为内部分数。\n- 证据：risks.scoring_risks | art_fcb598a7a7ed4b2482ca92474f06efe2 | https://github.com/vitalops/datatune#readme | no_demo; severity=medium\n\n## 6. 维护坑 · issue/PR 响应质量未知\n\n- 严重度：low\n- 证据强度：source_linked\n- 发现：issue_or_pr_quality=unknown。\n- 对用户的影响：用户无法判断遇到问题后是否有人维护。\n- 建议检查：抽样最近 issue/PR，判断是否长期无人处理。\n- 防护动作：issue/PR 响应未知时，必须提示维护风险。\n- 证据：evidence.maintainer_signals | art_fcb598a7a7ed4b2482ca92474f06efe2 | https://github.com/vitalops/datatune#readme | issue_or_pr_quality=unknown\n\n## 7. 维护坑 · 发布节奏不明确\n\n- 严重度：low\n- 证据强度：source_linked\n- 发现：release_recency=unknown。\n- 对用户的影响：安装命令和文档可能落后于代码，用户踩坑概率升高。\n- 建议检查：确认最近 release/tag 和 README 安装命令是否一致。\n- 防护动作：发布节奏未知或过期时，安装说明必须标注可能漂移。\n- 证据：evidence.maintainer_signals | art_fcb598a7a7ed4b2482ca92474f06efe2 | https://github.com/vitalops/datatune#readme | release_recency=unknown\n\n<!-- canonical_name: vitalops/datatune; human_manual_source: deepwiki_human_wiki -->\n",
      "markdown_key": "datatune",
      "pages": "draft",
      "source_refs": [
        {
          "evidence_id": "art_fcb598a7a7ed4b2482ca92474f06efe2",
          "kind": "docs",
          "supports_claim_ids": [
            "claim_identity",
            "claim_distribution",
            "claim_capability"
          ],
          "url": "https://github.com/vitalops/datatune#readme"
        }
      ],
      "summary": "DeepWiki/Human Wiki 完整输出，末尾追加 Discovery Agent 踩坑日志。",
      "title": "datatune 说明书",
      "toc": [
        "https://github.com/vitalops/datatune 项目说明书",
        "目录",
        "Datatune简介",
        "概述",
        "核心架构",
        "LLM支持",
        "数据处理原语",
        "Agent智能代理",
        "Doramagic 踩坑日志"
      ]
    }
  },
  "quality_gate": {
    "blocking_gaps": [],
    "category_confidence": "medium",
    "compile_status": "ready_for_review",
    "five_assets_present": true,
    "install_sandbox_verified": true,
    "missing_evidence": [],
    "next_action": "publish to Doramagic.ai project surfaces",
    "prompt_preview_boundary_ok": true,
    "publish_status": "publishable",
    "quick_start_verified": true,
    "repo_clone_verified": false,
    "repo_commit": null,
    "repo_inspection_error": null,
    "repo_inspection_files": [],
    "repo_inspection_verified": false,
    "review_reasons": [],
    "tag_count_ok": true,
    "unsupported_claims": []
  },
  "schema_version": "0.1",
  "user_assets": {
    "ai_context_pack": {
      "asset_id": "ai_context_pack",
      "filename": "AI_CONTEXT_PACK.md",
      "markdown": "# datatune - Doramagic AI Context Pack\n\n> 定位：安装前体验与判断资产。它帮助宿主 AI 有一个好的开始，但不代表已经安装、执行或验证目标项目。\n\n## 充分原则\n\n- **充分原则，不是压缩原则**：AI Context Pack 应该充分到让宿主 AI 在开工前理解项目价值、能力边界、使用入口、风险和证据来源；它可以分层组织，但不以最短摘要为目标。\n- **压缩策略**：只压缩噪声和重复内容，不压缩会影响判断和开工质量的上下文。\n\n## 给宿主 AI 的使用方式\n\n你正在读取 Doramagic 为 datatune 编译的 AI Context Pack。请把它当作开工前上下文：帮助用户理解适合谁、能做什么、如何开始、哪些必须安装后验证、风险在哪里。不要声称你已经安装、运行或执行了目标项目。\n\n## Claim 消费规则\n\n- **事实来源**：Repo Evidence + Claim/Evidence Graph；Human Wiki 只提供显著性、术语和叙事结构。\n- **事实最低状态**：`supported`\n- `supported`：可以作为项目事实使用，但回答中必须引用 claim_id 和证据路径。\n- `weak`：只能作为低置信度线索，必须要求用户继续核实。\n- `inferred`：只能用于风险提示或待确认问题，不能包装成项目事实。\n- `unverified`：不得作为事实使用，应明确说证据不足。\n- `contradicted`：必须展示冲突来源，不得替用户强行选择一个版本。\n\n## 它最适合谁\n\n- **想在安装前理解开源项目价值和边界的用户**：当前证据主要来自项目文档。 证据：`README.md` Claim：`clm_0002` supported 0.86\n\n## 它能做什么\n\n- **命令行启动或安装流程**（需要安装后验证）：项目文档中存在可执行命令，真实使用需要在本地或宿主环境中运行这些命令。 证据：`README.md` Claim：`clm_0001` supported 0.86\n\n## 怎么开始\n\n- `pip install datatune` 证据：`README.md` Claim：`clm_0003` supported 0.86\n\n## 继续前判断卡\n\n- **当前建议**：仅建议沙盒试装\n- **为什么**：项目存在安装命令、宿主配置或本地写入线索，不建议直接进入主力环境，应先在隔离环境试装。\n\n### 30 秒判断\n\n- **现在怎么做**：仅建议沙盒试装\n- **最小安全下一步**：先跑 Prompt Preview；若仍要安装，只在隔离环境试装\n- **先别相信**：真实输出质量不能在安装前相信。\n- **继续会触碰**：命令执行、本地环境或项目文件、宿主 AI 上下文\n\n### 现在可以相信\n\n- **适合人群线索：想在安装前理解开源项目价值和边界的用户**（supported）：有 supported claim 或项目证据支撑，但仍不等于真实安装效果。 证据：`README.md` Claim：`clm_0002` supported 0.86\n- **能力存在：命令行启动或安装流程**（supported）：可以相信项目包含这类能力线索；是否适合你的具体任务仍要试用或安装后验证。 证据：`README.md` Claim：`clm_0001` supported 0.86\n- **存在 Quick Start / 安装命令线索**（supported）：可以相信项目文档出现过启动或安装入口；不要因此直接在主力环境运行。 证据：`README.md` Claim：`clm_0003` supported 0.86\n\n### 现在还不能相信\n\n- **真实输出质量不能在安装前相信。**（unverified）：Prompt Preview 只能展示引导方式，不能证明真实项目中的结果质量。\n- **宿主 AI 版本兼容性不能在安装前相信。**（unverified）：Claude、Cursor、Codex、Gemini 等宿主加载规则和版本差异必须在真实环境验证。\n- **不会污染现有宿主 AI 行为，不能直接相信。**（inferred）：Skill、plugin、AGENTS/CLAUDE/GEMINI 指令可能改变宿主 AI 的默认行为。\n- **可安全回滚不能默认相信。**（unverified）：除非项目明确提供卸载和恢复说明，否则必须先在隔离环境验证。\n- **真实安装后是否与用户当前宿主 AI 版本兼容？**（unverified）：兼容性只能通过实际宿主环境验证。\n- **项目输出质量是否满足用户具体任务？**（unverified）：安装前预览只能展示流程和边界，不能替代真实评测。\n- **安装命令是否需要网络、权限或全局写入？**（unverified）：这影响企业环境和个人环境的安装风险。 证据：`README.md`\n\n### 继续会触碰什么\n\n- **命令执行**：包管理器、网络下载、本地插件目录、项目配置或用户主目录。 原因：运行第一条命令就可能产生环境改动；必须先判断是否值得跑。 证据：`README.md`\n- **本地环境或项目文件**：安装结果、插件缓存、项目配置或本地依赖目录。 原因：安装前无法证明写入范围和回滚方式，需要隔离验证。 证据：`README.md`\n- **宿主 AI 上下文**：AI Context Pack、Prompt Preview、Skill 路由、风险规则和项目事实。 原因：导入上下文会影响宿主 AI 后续判断，必须避免把未验证项包装成事实。\n\n### 最小安全下一步\n\n- **先跑 Prompt Preview**：用安装前交互式试用判断工作方式是否匹配，不需要授权或改环境。（适用：任何项目都适用，尤其是输出质量未知时。）\n- **只在隔离目录或测试账号试装**：避免安装命令污染主力宿主 AI、真实项目或用户主目录。（适用：存在命令执行、插件配置或本地写入线索时。）\n- **安装后只验证一个最小任务**：先验证加载、兼容、输出质量和回滚，再决定是否深用。（适用：准备从试用进入真实工作流时。）\n\n### 退出方式\n\n- **保留安装前状态**：记录原始宿主配置和项目状态，后续才能判断是否可恢复。\n- **记录安装命令和写入路径**：没有明确卸载说明时，至少要知道哪些目录或配置需要手动清理。\n- **如果没有回滚路径，不进入主力环境**：不可回滚是继续前阻断项，不应靠信任或运气继续。\n\n## 哪些只能预览\n\n- 解释项目适合谁和能做什么\n- 基于项目文档演示典型对话流程\n- 帮助用户判断是否值得安装或继续研究\n\n## 哪些必须安装后验证\n\n- 真实安装 Skill、插件或 CLI\n- 执行脚本、修改本地文件或访问外部服务\n- 验证真实输出质量、性能和兼容性\n\n## 边界与风险判断卡\n\n- **把安装前预览误认为真实运行**：用户可能高估项目已经完成的配置、权限和兼容性验证。 处理方式：明确区分 prompt_preview_can_do 与 runtime_required。 Claim：`clm_0004` inferred 0.45\n- **命令执行会修改本地环境**：安装命令可能写入用户主目录、宿主插件目录或项目配置。 处理方式：先在隔离环境或测试账号中运行。 证据：`README.md` Claim：`clm_0005` supported 0.86\n- **待确认**：真实安装后是否与用户当前宿主 AI 版本兼容？。原因：兼容性只能通过实际宿主环境验证。\n- **待确认**：项目输出质量是否满足用户具体任务？。原因：安装前预览只能展示流程和边界，不能替代真实评测。\n- **待确认**：安装命令是否需要网络、权限或全局写入？。原因：这影响企业环境和个人环境的安装风险。\n\n## 开工前工作上下文\n\n### 加载顺序\n\n- 先读取 how_to_use.host_ai_instruction，建立安装前判断资产的边界。\n- 读取 claim_graph_summary，确认事实来自 Claim/Evidence Graph，而不是 Human Wiki 叙事。\n- 再读取 intended_users、capabilities 和 quick_start_candidates，判断用户是否匹配。\n- 需要执行具体任务时，优先查 role_skill_index，再查 evidence_index。\n- 遇到真实安装、文件修改、网络访问、性能或兼容性问题时，转入 risk_card 和 boundaries.runtime_required。\n\n### 任务路由\n\n- **命令行启动或安装流程**：先说明这是安装后验证能力，再给出安装前检查清单。 边界：必须真实安装或运行后验证。 证据：`README.md` Claim：`clm_0001` supported 0.86\n\n### 上下文规模\n\n- 文件总数：50\n- 重要文件覆盖：23/50\n- 证据索引条目：23\n- 角色 / Skill 条目：9\n\n### 证据不足时的处理\n\n- **missing_evidence**：说明证据不足，要求用户提供目标文件、README 段落或安装后验证记录；不要补全事实。\n- **out_of_scope_request**：说明该任务超出当前 AI Context Pack 证据范围，并建议用户先查看 Human Manual 或真实安装后验证。\n- **runtime_request**：给出安装前检查清单和命令来源，但不要替用户执行命令或声称已执行。\n- **source_conflict**：同时展示冲突来源，标记为待核实，不要强行选择一个版本。\n\n## Prompt Recipes\n\n### 适配判断\n\n- 目标：判断这个项目是否适合用户当前任务。\n- 预期输出：适配结论、关键理由、证据引用、安装前可预览内容、必须安装后验证内容、下一步建议。\n\n```text\n请基于 datatune 的 AI Context Pack，先问我 3 个必要问题，然后判断它是否适合我的任务。回答必须包含：适合谁、能做什么、不能做什么、是否值得安装、证据来自哪里。所有项目事实必须引用 evidence_refs、source_paths 或 claim_id。\n```\n\n### 安装前体验\n\n- 目标：让用户在安装前感受核心工作流，同时避免把预览包装成真实能力或营销承诺。\n- 预期输出：一段带边界标签的体验剧本、安装后验证清单和谨慎建议；不含真实运行承诺或强营销表述。\n\n```text\n请把 datatune 当作安装前体验资产，而不是已安装工具或真实运行环境。\n\n请严格输出四段：\n1. 先问我 3 个必要问题。\n2. 给出一段“体验剧本”：用 [安装前可预览]、[必须安装后验证]、[证据不足] 三种标签展示它可能如何引导工作流。\n3. 给出安装后验证清单：列出哪些能力只有真实安装、真实宿主加载、真实项目运行后才能确认。\n4. 给出谨慎建议：只能说“值得继续研究/试装”“先补充信息后再判断”或“不建议继续”，不得替项目背书。\n\n硬性边界：\n- 不要声称已经安装、运行、执行测试、修改文件或产生真实结果。\n- 不要写“自动适配”“确保通过”“完美适配”“强烈建议安装”等承诺性表达。\n- 如果描述安装后的工作方式，必须使用“如果安装成功且宿主正确加载 Skill，它可能会……”这种条件句。\n- 体验剧本只能写成“示例台词/假设流程”：使用“可能会询问/可能会建议/可能会展示”，不要写“已写入、已生成、已通过、正在运行、正在生成”。\n- Prompt Preview 不负责给安装命令；如用户准备试装，只能提示先阅读 Quick Start 和 Risk Card，并在隔离环境验证。\n- 所有项目事实必须来自 supported claim、evidence_refs 或 source_paths；inferred/unverified 只能作风险或待确认项。\n\n```\n\n### 角色 / Skill 选择\n\n- 目标：从项目里的角色或 Skill 中挑选最匹配的资产。\n- 预期输出：候选角色或 Skill 列表，每项包含适用场景、证据路径、风险边界和是否需要安装后验证。\n\n```text\n请读取 role_skill_index，根据我的目标任务推荐 3-5 个最相关的角色或 Skill。每个推荐都要说明适用场景、可能输出、风险边界和 evidence_refs。\n```\n\n### 风险预检\n\n- 目标：安装或引入前识别环境、权限、规则冲突和质量风险。\n- 预期输出：环境、权限、依赖、许可、宿主冲突、质量风险和未知项的检查清单。\n\n```text\n请基于 risk_card、boundaries 和 quick_start_candidates，给我一份安装前风险预检清单。不要替我执行命令，只说明我应该检查什么、为什么检查、失败会有什么影响。\n```\n\n### 宿主 AI 开工指令\n\n- 目标：把项目上下文转成一次对话开始前的宿主 AI 指令。\n- 预期输出：一段边界明确、证据引用明确、适合复制给宿主 AI 的开工前指令。\n\n```text\n请基于 datatune 的 AI Context Pack，生成一段我可以粘贴给宿主 AI 的开工前指令。这段指令必须遵守 not_runtime=true，不能声称项目已经安装、运行或产生真实结果。\n```\n\n\n## 角色 / Skill 索引\n\n- 共索引 9 个角色 / Skill / 项目文档条目。\n\n- **🎵 Datatune**（project_doc）：! PyPI version https://img.shields.io/pypi/v/datatune.svg https://pypi.org/project/datatune/ ! License https://img.shields.io/github/license/vitalops/datatune https://github.com/vitalops/datatune/blob/main/LICENSE ! PyPI Downloads https://static.pepy.tech/badge/datatune https://pepy.tech/projects/datatune ! Docs https://img.shields.io/badge/docs-docs.datatune.ai-blue https://docs.datatune.ai ! Discord https://img.sh… 激活提示：当用户需要理解项目结构、安装方式或边界时参考。 证据：`README.md`\n- **Agents**（project_doc）：Datatune Agent allows large language models LLMs to autonomously plan and execute data transformation steps using natural language prompts. 激活提示：当用户需要理解项目结构、安装方式或边界时参考。 证据：`docs/source/Agent.md`\n- **Data Loaders**（project_doc）：Datatune supports multiple data loading backends to work with your data efficiently. Choose the backend that best fits your use case and infrastructure. 激活提示：当用户需要理解项目结构、安装方式或边界时参考。 证据：`docs/source/DataLoaders.md`\n- **Filter**（project_doc）：The Filter operation in Datatune uses LLMs to evaluate and filter rows in a dataset based on natural language criteria. 激活提示：当用户需要理解项目结构、安装方式或边界时参考。 证据：`docs/source/Filter.md`\n- **LLM**（project_doc）：The LLM module in Datatune provides classes for interfacing with various Language Model providers. It handles the connection to and inference from LLMs with a unified API structure, supporting both single and batch inference, with the help of LiteLLM. 激活提示：当用户需要理解项目结构、安装方式或边界时参考。 证据：`docs/source/LLM.md`\n- **Map**（project_doc）：The Map operation in Datatune uses LLMs to transform data in a dataset by generating new fields or modifying existing ones based on natural language instructions. 激活提示：当用户需要理解项目结构、安装方式或边界时参考。 证据：`docs/source/Map.md`\n- **Op**（project_doc）：The Op module provides the base operation class and utility functions for Datatune's data transformation pipeline. 激活提示：当用户需要理解项目结构、安装方式或边界时参考。 证据：`docs/source/Op.md`\n- **Reduce**（project_doc）：The Reduce operation in Datatune reduces the size of a dataset by grouping, deduplicating, or otherwise collapsing rows based on a specified action. It is typically used to minimize downstream processing cost e.g. LLM calls by identifying canonical rows and eliminating redundant ones. 激活提示：当用户需要理解项目结构、安装方式或边界时参考。 证据：`docs/source/Reduce.md`\n- **Documentation**（project_doc）：Perform transformations on your data with natural language using LLMs 激活提示：当用户需要理解项目结构、安装方式或边界时参考。 证据：`docs/source/index.md`\n\n## 证据索引\n\n- 共索引 23 条证据。\n\n- **🎵 Datatune**（documentation）：! PyPI version https://img.shields.io/pypi/v/datatune.svg https://pypi.org/project/datatune/ ! License https://img.shields.io/github/license/vitalops/datatune https://github.com/vitalops/datatune/blob/main/LICENSE ! PyPI Downloads https://static.pepy.tech/badge/datatune https://pepy.tech/projects/datatune ! Docs https://img.shields.io/badge/docs-docs.datatune.ai-blue https://docs.datatune.ai ! Discord https://img.shields.io/badge/Discord-7289da?logo=discord&logoColor=white https://discord.gg/3RKA5AryQX 证据：`README.md`\n- **License**（source_file）：Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files the \"Software\" , to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 证据：`LICENSE`\n- **Agents**（documentation）：Datatune Agent allows large language models LLMs to autonomously plan and execute data transformation steps using natural language prompts. 证据：`docs/source/Agent.md`\n- **Data Loaders**（documentation）：Datatune supports multiple data loading backends to work with your data efficiently. Choose the backend that best fits your use case and infrastructure. 证据：`docs/source/DataLoaders.md`\n- **Filter**（documentation）：The Filter operation in Datatune uses LLMs to evaluate and filter rows in a dataset based on natural language criteria. 证据：`docs/source/Filter.md`\n- **LLM**（documentation）：The LLM module in Datatune provides classes for interfacing with various Language Model providers. It handles the connection to and inference from LLMs with a unified API structure, supporting both single and batch inference, with the help of LiteLLM. 证据：`docs/source/LLM.md`\n- **Map**（documentation）：The Map operation in Datatune uses LLMs to transform data in a dataset by generating new fields or modifying existing ones based on natural language instructions. 证据：`docs/source/Map.md`\n- **Op**（documentation）：The Op module provides the base operation class and utility functions for Datatune's data transformation pipeline. 证据：`docs/source/Op.md`\n- **Reduce**（documentation）：The Reduce operation in Datatune reduces the size of a dataset by grouping, deduplicating, or otherwise collapsing rows based on a specified action. It is typically used to minimize downstream processing cost e.g. LLM calls by identifying canonical rows and eliminating redundant ones. 证据：`docs/source/Reduce.md`\n- **Documentation**（documentation）：Perform transformations on your data with natural language using LLMs 证据：`docs/source/index.md`\n- **Byte-compiled / optimized / DLL files**（source_file）：/tests/test data/ .DS Store Byte-compiled / optimized / DLL files pycache / .py cod $py.class 证据：`.gitignore`\n- **Cname**（source_file）：docs.datatune.ai 证据：`CNAME`\n- **Init**（source_file）：from datatune.agent.agent import Agent from datatune.core.filter import filter from datatune.core.map import map from datatune.core.dask.op import finalize from datatune.core.deduplication import SemanticDeduplicator from datatune.core.reduce import reduce all = \"map\", \"filter\", \"finalize\", \"Agent\", \"reduce\" 证据：`datatune/__init__.py`\n- **Logger**（source_file）：import logging import threading import time import sys 证据：`datatune/logger.py`\n- **Minimal makefile for Sphinx documentation**（source_file）：Minimal makefile for Sphinx documentation 证据：`docs/Makefile`\n- **Make**（source_file）：REM Command file for Sphinx documentation 证据：`docs/make.bat`\n- **Getting Started**（source_file）：{ \"cells\": { \"cell type\": \"markdown\", \"metadata\": { \"id\": \"OHg0Xg632UxE\" }, \"source\": \" Getting started with Datatune\" }, { \"cell type\": \"markdown\", \"metadata\": { \"id\": \"TjDiMfzi2UxF\" }, \"source\": \" Setup and Installation \\n\", \"First, let's install the necessary packages:\" }, { \"cell type\": \"code\", \"execution count\": null, \"metadata\": { \"vscode\": { \"languageId\": \"plaintext\" }, \"id\": \"aISH8Ve42UxF\" }, \"outputs\": , \"source\": \"!pip install datatune\" }, { \"cell type\": \"markdown\", \"metadata\": { \"id\": \"Gd16ZTSG2UxG\" }, \"source\": \" Import Required Libraries\\n\" }, { \"cell type\": \"code\", \"execution count\": null, \"metadata\": { \"id\": \"x2GlLBRv2UxG\" }, \"outputs\": , \"source\": \"import pandas as pd\\n\", \"i… 证据：`examples/Getting_started.ipynb`\n- **Agents**（source_file）：{ \"nbformat\": 4, \"nbformat minor\": 0, \"metadata\": { \"colab\": { \"provenance\": }, \"kernelspec\": { \"name\": \"python3\", \"display name\": \"Python 3\" }, \"language info\": { \"name\": \"python\" } }, \"cells\": { \"cell type\": \"code\", \"execution count\": null, \"metadata\": { \"id\": \"0olYqjotD-UR\" }, \"outputs\": , \"source\": \" Let's start by installing libraries\" }, { \"cell type\": \"code\", \"source\": \"!pip install datatune\" , \"metadata\": { \"collapsed\": true, \"id\": \"S5lfcBmNHwz1\" }, \"execution count\": null, \"outputs\": }, { \"cell type\": \"code\", \"source\": \"import os\\n\", \"import dask.dataframe as dd\\n\", \"import datatune as dt\\n\", \"from datatune.llm.llm import Azure\" , \"metadata\": { \"id\": \"-NMdOHHzHsRa\" }, \"execution co… 证据：`examples/agents.ipynb`\n- **Data Anonymization**（source_file）：{ \"nbformat\": 4, \"nbformat minor\": 0, \"metadata\": { \"colab\": { \"provenance\": }, \"kernelspec\": { \"name\": \"python3\", \"display name\": \"Python 3\" }, \"language info\": { \"name\": \"python\" } }, \"cells\": { \"cell type\": \"code\", \"source\": \"!pip install datatune\" , \"metadata\": { \"colab\": { \"base uri\": \"https://localhost:8080/\" }, \"collapsed\": true, \"id\": \"VCfT3UnVOXrb\", \"outputId\": \"491f3d3e-8157-4336-e157-f6dbced4d555\" }, \"execution count\": 1, \"outputs\": { \"output type\": \"stream\", \"name\": \"stdout\", \"text\": \"Collecting datatune\\n\", \" Downloading datatune-0.0.4-py3-none-any.whl.metadata 10 kB \\n\", \"Requirement already satisfied: numpy in /usr/local/lib/python3.12/dist-packages from datatune 2.0.2 \\n\", \"… 证据：`examples/data_anonymization.ipynb`\n- **Deduplication**（source_file）：{ \"cells\": { \"cell type\": \"markdown\", \"id\": \"9d96f064\", \"metadata\": {}, \"source\": \" Datatune with Semantic Deduplication\" }, { \"cell type\": \"markdown\", \"id\": \"e16bfaa5\", \"metadata\": {}, \"source\": \"Let's start by installing dependencies. We will be using duckdb as our database backend and OpenAI LLM API.\" }, { \"cell type\": \"code\", \"execution count\": null, \"id\": \"4f08aea7\", \"metadata\": {}, \"outputs\": , \"source\": \"!pip install datatune\" }, { \"cell type\": \"markdown\", \"id\": \"6b326c27\", \"metadata\": {}, \"source\": \" Import required libraries\" }, { \"cell type\": \"code\", \"execution count\": null, \"id\": \"5169a466\", \"metadata\": {}, \"outputs\": , \"source\": \"import datatune as dt\\n\", \"import seaborn as sns\\… 证据：`examples/deduplication.ipynb`\n- **Ibis Core**（source_file）：{ \"cells\": { \"cell type\": \"markdown\", \"id\": \"9d96f064\", \"metadata\": {}, \"source\": \" Datatune with Ibis\" }, { \"cell type\": \"markdown\", \"id\": \"e16bfaa5\", \"metadata\": {}, \"source\": \"Let's start by installing dependencies. We will be using duckdb as our database backend and OpenAI LLM API.\" }, { \"cell type\": \"code\", \"execution count\": null, \"id\": \"4f08aea7\", \"metadata\": {}, \"outputs\": , \"source\": \"!pip install -e ../. ibis \" }, { \"cell type\": \"markdown\", \"id\": \"6b326c27\", \"metadata\": {}, \"source\": \" Import required libraries\" }, { \"cell type\": \"code\", \"execution count\": null, \"id\": \"5169a466\", \"metadata\": {}, \"outputs\": , \"source\": \"import ibis\\n\", \"import datatune as dt\\n\", \"import seaborn as… 证据：`examples/ibis_core.ipynb`\n- **Pyproject**（source_file）：build-system requires = \"setuptools =61.0\", \"wheel\" build-backend = \"setuptools.build meta\" 证据：`pyproject.toml`\n- **Ibis Core Test**（source_file）：import pytest import ibis import pandas as pd import datatune as dt 证据：`tests/ibis_core_test.py`\n\n## 宿主 AI 必须遵守的规则\n\n- **把本资产当作开工前上下文，而不是运行环境。**：AI Context Pack 只包含证据化项目理解，不包含目标项目的可执行状态。 证据：`README.md`, `LICENSE`, `docs/source/Agent.md`\n- **回答用户时区分可预览内容与必须安装后才能验证的内容。**：安装前体验的消费者价值来自降低误装和误判，而不是伪装成真实运行。 证据：`README.md`, `LICENSE`, `docs/source/Agent.md`\n\n## 用户开工前应该回答的问题\n\n- 你准备在哪个宿主 AI 或本地环境中使用它？\n- 你只是想先体验工作流，还是准备真实安装？\n- 你最在意的是安装成本、输出质量、还是和现有规则的冲突？\n\n## 验收标准\n\n- 所有能力声明都能回指到 evidence_refs 中的文件路径。\n- AI_CONTEXT_PACK.md 没有把预览包装成真实运行。\n- 用户能在 3 分钟内看懂适合谁、能做什么、如何开始和风险边界。\n\n---\n\n## Doramagic Context Augmentation\n\n下面内容用于强化 Repomix/AI Context Pack 主体。Human Manual 只提供阅读骨架；踩坑日志会被转成宿主 AI 必须遵守的工作约束。\n\n## Human Manual 骨架\n\n使用规则：这里只是项目阅读路线和显著性信号，不是事实权威。具体事实仍必须回到 repo evidence / Claim Graph。\n\n宿主 AI 硬性规则：\n- 不得把页标题、章节顺序、摘要或 importance 当作项目事实证据。\n- 解释 Human Manual 骨架时，必须明确说它只是阅读路线/显著性信号。\n- 能力、安装、兼容性、运行状态和风险判断必须引用 repo evidence、source path 或 Claim Graph。\n\n- **Datatune简介**：importance `high`\n  - source_paths: README.md, datatune/__init__.py\n- **快速开始**：importance `high`\n  - source_paths: pyproject.toml, examples/Getting_started.ipynb\n- **系统架构**：importance `high`\n  - source_paths: datatune/agent/agent.py, datatune/agent/runtime.py, datatune/core/map.py, datatune/core/filter.py, datatune/core/reduce.py\n- **数据管理与数据流**：importance `medium`\n  - source_paths: datatune/core/dask/map_dask.py, datatune/core/dask/filter_dask.py, datatune/core/ibis/map_ibis.py, datatune/core/ibis/filter_ibis.py\n- **Map操作**：importance `high`\n  - source_paths: datatune/core/map.py, datatune/core/dask/map_dask.py, datatune/core/ibis/map_ibis.py, docs/source/Map.md\n- **Filter操作**：importance `high`\n  - source_paths: datatune/core/filter.py, datatune/core/dask/filter_dask.py, datatune/core/ibis/filter_ibis.py, docs/source/Filter.md\n- **Reduce操作**：importance `medium`\n  - source_paths: datatune/core/reduce.py, docs/source/Reduce.md\n- **Agent系统**：importance `high`\n  - source_paths: datatune/agent/agent.py, datatune/agent/runtime.py, examples/agents.ipynb, docs/source/Agent.md\n\n## Repo Inspection Evidence / 源码检查证据\n\n- repo_clone_verified: false\n- repo_inspection_verified: false\n- repo_commit: `unknown`\n\n宿主 AI 硬性规则：\n- 没有 repo_clone_verified=true 时，不得声称已经读过源码。\n- 没有 repo_inspection_verified=true 时，不得把 README/docs/package 文件判断写成事实。\n- 没有 quick_start_verified=true 时，不得声称 Quick Start 已跑通。\n\n## Doramagic Pitfall Constraints / 踩坑约束\n\n这些规则来自 Doramagic 发现、验证或编译过程中的项目专属坑点。宿主 AI 必须把它们当作工作约束，而不是普通说明文字。\n\n### Constraint 1: 能力判断依赖假设\n\n- Trigger: README/documentation is current enough for a first validation pass.\n- Host AI rule: 将假设转成下游验证清单。\n- Why it matters: 假设不成立时，用户拿不到承诺的能力。\n- Evidence: capability.assumptions | art_fcb598a7a7ed4b2482ca92474f06efe2 | https://github.com/vitalops/datatune#readme | README/documentation is current enough for a first validation pass.\n- Hard boundary: 不要把这个坑点包装成已解决、已验证或可忽略，除非后续验证证据明确证明它已经关闭。\n\n### Constraint 2: 维护活跃度未知\n\n- Trigger: 未记录 last_activity_observed。\n- Host AI rule: 补 GitHub 最近 commit、release、issue/PR 响应信号。\n- Why it matters: 新项目、停更项目和活跃项目会被混在一起，推荐信任度下降。\n- Evidence: evidence.maintainer_signals | art_fcb598a7a7ed4b2482ca92474f06efe2 | https://github.com/vitalops/datatune#readme | last_activity_observed missing\n- Hard boundary: 不要把这个坑点包装成已解决、已验证或可忽略，除非后续验证证据明确证明它已经关闭。\n\n### Constraint 3: 下游验证发现风险项\n\n- Trigger: no_demo\n- Host AI rule: 进入安全/权限治理复核队列。\n- Why it matters: 下游已经要求复核，不能在页面中弱化。\n- Evidence: downstream_validation.risk_items | art_fcb598a7a7ed4b2482ca92474f06efe2 | https://github.com/vitalops/datatune#readme | no_demo; severity=medium\n- Hard boundary: 不要把这个坑点包装成已解决、已验证或可忽略，除非后续验证证据明确证明它已经关闭。\n\n### Constraint 4: 存在安全注意事项\n\n- Trigger: No sandbox install has been executed yet; downstream must verify before user use.\n- Host AI rule: 转成明确权限清单和安全审查提示。\n- Why it matters: 用户安装前需要知道权限边界和敏感操作。\n- Evidence: risks.safety_notes | art_fcb598a7a7ed4b2482ca92474f06efe2 | https://github.com/vitalops/datatune#readme | No sandbox install has been executed yet; downstream must verify before user use.\n- Hard boundary: 不要把这个坑点包装成已解决、已验证或可忽略，除非后续验证证据明确证明它已经关闭。\n\n### Constraint 5: 存在评分风险\n\n- Trigger: no_demo\n- Host AI rule: 把风险写入边界卡，并确认是否需要人工复核。\n- Why it matters: 风险会影响是否适合普通用户安装。\n- Evidence: risks.scoring_risks | art_fcb598a7a7ed4b2482ca92474f06efe2 | https://github.com/vitalops/datatune#readme | no_demo; severity=medium\n- Hard boundary: 不要把这个坑点包装成已解决、已验证或可忽略，除非后续验证证据明确证明它已经关闭。\n\n### Constraint 6: issue/PR 响应质量未知\n\n- Trigger: issue_or_pr_quality=unknown。\n- Host AI rule: 抽样最近 issue/PR，判断是否长期无人处理。\n- Why it matters: 用户无法判断遇到问题后是否有人维护。\n- Evidence: evidence.maintainer_signals | art_fcb598a7a7ed4b2482ca92474f06efe2 | https://github.com/vitalops/datatune#readme | issue_or_pr_quality=unknown\n- Hard boundary: 不要把这个坑点包装成已解决、已验证或可忽略，除非后续验证证据明确证明它已经关闭。\n\n### Constraint 7: 发布节奏不明确\n\n- Trigger: release_recency=unknown。\n- Host AI rule: 确认最近 release/tag 和 README 安装命令是否一致。\n- Why it matters: 安装命令和文档可能落后于代码，用户踩坑概率升高。\n- Evidence: evidence.maintainer_signals | art_fcb598a7a7ed4b2482ca92474f06efe2 | https://github.com/vitalops/datatune#readme | release_recency=unknown\n- Hard boundary: 不要把这个坑点包装成已解决、已验证或可忽略，除非后续验证证据明确证明它已经关闭。\n",
      "summary": "给宿主 AI 的上下文和工作边界。",
      "title": "AI Context Pack / 带给我的 AI"
    },
    "boundary_risk_card": {
      "asset_id": "boundary_risk_card",
      "filename": "BOUNDARY_RISK_CARD.md",
      "markdown": "# Boundary & Risk Card / 安装前决策卡\n\n项目：vitalops/datatune\n\n## Doramagic 试用结论\n\n当前结论：可以进入发布前推荐检查；首次使用仍应从最小权限、临时目录和可回滚配置开始。\n\n## 用户现在可以做\n\n- 可以先阅读 Human Manual，理解项目目的和主要工作流。\n- 可以复制 Prompt Preview 做安装前体验；这只验证交互感，不代表真实运行。\n- 可以把官方 Quick Start 命令放到隔离环境中验证，不要直接进主力环境。\n\n## 现在不要做\n\n- 不要把 Prompt Preview 当成项目实际运行结果。\n- 不要把 metadata-only validation 当成沙箱安装验证。\n- 不要把未验证能力写成“已支持、已跑通、可放心安装”。\n- 不要在首次试用时交出生产数据、私人文件、真实密钥或主力配置目录。\n\n## 安装前检查\n\n- 宿主 AI 是否匹配：chatgpt\n- 官方安装入口状态：已发现官方入口\n- 是否在临时目录、临时宿主或容器中验证：必须是\n- 是否能回滚配置改动：必须能\n- 是否需要 API Key、网络访问、读写文件或修改宿主配置：未确认前按高风险处理\n- 是否记录了安装命令、实际输出和失败日志：必须记录\n\n## 当前阻塞项\n\n- 无阻塞项。\n\n## 项目专属踩坑\n\n- 能力判断依赖假设（medium）：假设不成立时，用户拿不到承诺的能力。 建议检查：将假设转成下游验证清单。\n- 维护活跃度未知（medium）：新项目、停更项目和活跃项目会被混在一起，推荐信任度下降。 建议检查：补 GitHub 最近 commit、release、issue/PR 响应信号。\n- 下游验证发现风险项（medium）：下游已经要求复核，不能在页面中弱化。 建议检查：进入安全/权限治理复核队列。\n- 存在安全注意事项（medium）：用户安装前需要知道权限边界和敏感操作。 建议检查：转成明确权限清单和安全审查提示。\n- 存在评分风险（medium）：风险会影响是否适合普通用户安装。 建议检查：把风险写入边界卡，并确认是否需要人工复核。\n\n## 风险与权限提示\n\n- no_demo: medium\n\n## 证据缺口\n\n- 暂未发现结构化证据缺口。\n",
      "summary": "安装、权限、验证和推荐前风险。",
      "title": "Boundary & Risk Card / 边界与风险卡"
    },
    "human_manual": {
      "asset_id": "human_manual",
      "filename": "HUMAN_MANUAL.md",
      "markdown": "# https://github.com/vitalops/datatune 项目说明书\n\n生成时间：2026-05-14 07:26:20 UTC\n\n## 目录\n\n- [Datatune简介](#intro)\n- [快速开始](#quickstart)\n- [系统架构](#architecture)\n- [数据管理与数据流](#datamanagement)\n- [Map操作](#map-operation)\n- [Filter操作](#filter-operation)\n- [Reduce操作](#reduce-operation)\n- [Agent系统](#agent-system)\n- [LLM集成](#llm-integration)\n- [数据源支持](#datasource)\n\n<a id='intro'></a>\n\n## Datatune简介\n\n### 相关页面\n\n相关主题：[系统架构](#architecture), [快速开始](#quickstart)\n\n<details>\n<summary>相关源码文件</summary>\n\n以下源码文件用于生成本页说明：\n\n- [README.md](https://github.com/vitalops/datatune/blob/main/README.md)\n- [datatune/llm/llm.py](https://github.com/vitalops/datatune/blob/main/datatune/llm/llm.py)\n- [datatune/agent/agent.py](https://github.com/vitalops/datatune/blob/main/datatune/agent/agent.py)\n- [datatune/agent/__init__.py](https://github.com/vitalops/datatune/blob/main/datatune/agent/__init__.py)\n- [datatune/core/dask/map_dask.py](https://github.com/vitalops/datatune/blob/main/datatune/core/dask/map_dask.py)\n- [datatune/core/dask/filter_dask.py](https://github.com/vitalops/datatune/blob/main/datatune/core/dask/filter_dask.py)\n- [datatune/core/ibis/map_ibis.py](https://github.com/vitalops/datatune/blob/main/datatune/core/ibis/map_ibis.py)\n- [datatune/core/ibis/filter_ibis.py](https://github.com/vitalops/datatune/blob/main/datatune/core/ibis/filter_ibis.py)\n- [datatune/core/deduplication.py](https://github.com/vitalops/datatune/blob/main/datatune/core/deduplication.py)\n- [datatune/llm/model_rate_limits.py](https://github.com/vitalops/datatune/blob/main/datatune/llm/model_rate_limits.py)\n</details>\n\n# Datatune简介\n\n## 概述\n\nDatatune是一个基于大语言模型（LLM）的数据转换框架，它允许用户使用自然语言描述来对结构化数据进行转换、映射和过滤操作。该框架支持Dask和Ibis两种后端计算引擎，能够处理大规模数据集，并通过多种LLM提供商（如OpenAI、Ollama、Azure等）实现智能化的数据处理。\n\n资料来源：[README.md:1-20]()\n\n## 核心架构\n\nDatatune的架构主要由以下几个核心组件构成：\n\n### 2.1 组件层次\n\n```mermaid\ngraph TD\n    A[用户接口层] --> B[Agent智能代理]\n    A --> C[Primitive原语操作]\n    B --> C\n    C --> D[Map映射操作]\n    C --> E[Filter过滤操作]\n    D --> F[数据后端]\n    E --> F\n    F --> G[Dask后端]\n    F --> H[Ibis后端]\n    G --> I[DuckDB/PostgreSQL/BigQuery]\n    H --> I\n    F --> J[LLM调用层]\n    J --> K[OpenAI]\n    J --> L[Ollama]\n    J --> M[Azure]\n    J --> N[Mistral]\n    J --> O[Huggingface]\n    J --> P[VLLM]\n```\n\n### 2.2 核心模块说明\n\n| 模块路径 | 功能说明 |\n|---------|---------|\n| `datatune.llm.llm` | LLM基类和多种LLM提供商实现 |\n| `datatune.agent.agent` | Agent智能代理，支持自动规划数据转换流程 |\n| `datatune.core.dask.map_dask` | Dask后端的Map映射操作实现 |\n| `datatune.core.dask.filter_dask` | Dask后端的Filter过滤操作实现 |\n| `datatune.core.ibis.map_ibis` | Ibis后端的Map映射操作实现 |\n| `datatune.core.ibis.filter_ibis` | Ibis后端的Filter过滤操作实现 |\n| `datatune.core.deduplication` | 数据去重功能，支持基于嵌入向量的相似度检测 |\n\n资料来源：[datatune/llm/llm.py:1-150]()[datatune/agent/agent.py:1-100]()\n\n## LLM支持\n\n### 3.1 支持的LLM提供商\n\nDatatune通过litellm库统一封装了多种LLM提供商，用户可以根据需求选择合适的模型：\n\n资料来源：[datatune/llm/llm.py:45-120]()\n\n| 提供商 | 类名 | 默认模型 | 特点 |\n|-------|------|---------|------|\n| OpenAI | `OpenAI` | gpt-3.5-turbo | 云端API，需提供api_key |\n| Ollama | `Ollama` | gemma3:4b | 本地部署，支持gemma、llama等模型 |\n| Azure | `Azure` | gpt-3.5-turbo | Azure OpenAI服务 |\n| Mistral | `Mistral` | mistral-tiny | Mistral AI模型 |\n| Huggingface | `Huggingface` | - | Hugging Face推理端点 |\n| VLLM | `VLLM` | 用户指定 | 高性能本地推理服务 |\n\n### 3.2 模型速率限制\n\n框架内置了主流模型的速率限制配置，包括每分钟请求数（RPM）和每分钟令牌数（TPM）：\n\n资料来源：[datatune/llm/model_rate_limits.py:1-100]()\n\n| 模型 | RPM | TPM |\n|------|-----|-----|\n| gpt-3.5-turbo | 500 | 200,000 |\n| gpt-4 | 500 | 10,000 |\n| gpt-4-turbo | 500 | 30,000 |\n| gpt-4o | 500 | 30,000 |\n| gpt-4.1-mini | 500 | 200,000 |\n| gpt-4.1-nano | 500 | 200,000 |\n\n### 3.3 LLM基类实现\n\n```python\nclass LLM:\n    def __init__(self, model_name: str, **kwargs) -> None:\n        self.model_name = model_name  # 格式为 \"provider/model\"\n        self._base_model_name = model_name.split(\"/\", 1)[1]\n```\n\n所有LLM类都继承自基类`LLM`，通过litellm库实现统一的调用接口，并支持自定义速率限制参数。\n\n资料来源：[datatune/llm/llm.py:45-70]()\n\n## 数据处理原语\n\n### 4.1 Map映射操作\n\nMap操作用于从现有数据创建新的列，通过LLM理解并应用转换逻辑：\n\n```mermaid\ngraph LR\n    A[输入DataFrame] --> B[序列化输入列]\n    B --> C[LLM批量处理]\n    C --> D[解析LLM输出]\n    D --> E[反序列化到新列]\n    E --> F[输出DataFrame]\n```\n\n**Dask实现流程：**\n\n1. 序列化输入列数据\n2. 检测重复记录并建立映射关系\n3. 仅对主记录调用LLM\n4. 复制结果到重复记录\n5. 将LLM输出反序列化为新列\n\n资料来源：[datatune/core/dask/map_dask.py:1-80]()[datatune/core/ibis/map_ibis.py:1-60]()\n\n### 4.2 Filter过滤操作\n\nFilter操作用于根据条件移除不满足要求的行：\n\n```mermaid\ngraph LR\n    A[输入DataFrame] --> B[LLM决策判断]\n    B --> C[解析过滤决策]\n    C --> D[应用过滤条件]\n    D --> E[输出过滤后DataFrame]\n```\n\n**输出格式要求：**\n\nLLM返回的过滤决策必须遵循特定格式：\n\n```\nindex=<row_index>|{key1: value1, ..., '__filter__': True/False}<endofrow>\n```\n\n资料来源：[datatune/core/dask/filter_dask.py:1-50]()[datatune/core/ibis/filter_ibis.py:1-50]()\n\n### 4.3 去重功能\n\nDatatune提供了基于嵌入向量的智能去重功能：\n\n```mermaid\ngraph TD\n    A[原始数据] --> B[嵌入向量生成]\n    B --> C[FAISS索引构建]\n    C --> D[HNSW相似度搜索]\n    D --> E[聚类分组]\n    E --> F[主记录标识]\n```\n\n**核心参数：**\n\n| 参数 | 说明 | 默认值 |\n|------|------|-------|\n| embedding_model | 嵌入模型名称 | text-embedding-3-small |\n| sim_threshold | 相似度阈值 | 0.90 |\n| top_k | Top-K近邻数 | 50 |\n| hnsw_m | HNSW图的M参数 | 32 |\n| ef_search | 搜索时的ef参数 | 64 |\n\n资料来源：[datatune/core/deduplication.py:1-150]()\n\n## Agent智能代理\n\n### 5.1 代理概述\n\nAgent是Datatune的高级抽象，它能够自动分析用户的自然语言请求，规划并执行一系列数据转换步骤：\n\n```mermaid\ngraph TD\n    A[用户自然语言请求] --> B[LLM解析意图]\n    B --> C[生成执行计划]\n    C --> D[计划验证]\n    D --> E{计划有效?}\n    E -->|是| F[逐步执行]\n    E -->|否| G[返回错误]\n    F --> H[执行日志记录]\n    H --> I[完成并返回结果]\n```\n\n资料来源：[datatune/agent/agent.py:1-150]()[datatune/agent/__init__.py:1-40]()\n\n### 5.2 计划格式\n\nAgent生成的计划为JSON数组，每个步骤包含以下字段：\n\n| 字段 | 类型 | 说明 |\n|------|------|------|\n| type | string | 操作类型：dask 或 primitive |\n| operation | string | 操作名称 |\n| params | dict | 操作参数字典 |\n| subprompt | string | 原语操作的LLM提示词 |\n| input_fields | list | 输入列列表 |\n| output_fields | list | 输出列列表（仅Map） |\n\n资料来源：[datatune/agent/agent.py:100-150]()\n\n### 5.3 支持的Dask操作\n\nAgent可使用的Dask操作包括：\n\n| 操作类型 | 操作名 | 说明 |\n|---------|--------|------|\n| 列操作 | add_column | 从表达式创建新列 |\n| 列操作 | apply_function | 对列应用函数 |\n| 列操作 | rename_columns | 重命名列 |\n| 列操作 | astype_column | 更改列数据类型 |\n\n资料来源：[datatune/agent/agent.py:150-200]()\n\n## 数据源支持\n\n### 6.1 Dask后端\n\nDask后端支持大规模分布式数据处理：\n\n```python\nimport dask.dataframe as dd\ndf = dd.read_csv(\"data.csv\")\n```\n\n特点：\n- 支持分区分布式计算\n- 延迟执行提高性能\n- 与pandas API兼容\n\n### 6.2 Ibis后端\n\nIbis后端支持多种SQL数据库：\n\n| 数据库 | 连接方式 |\n|-------|---------|\n| DuckDB | `ibis.duckdb.connect()` |\n| PostgreSQL | `ibis.postgres.connect()` |\n| BigQuery | `ibis.bigquery.connect()` |\n\n资料来源：[README.md:30-60]()\n\n## 快速开始\n\n### 7.1 安装\n\n```bash\npip install datatune\n```\n\n### 7.2 基本使用示例\n\n```python\nimport datatune as dt\nfrom datatune.llm.llm import OpenAI\nimport dask.dataframe as dd\n\nllm = OpenAI(model_name=\"gpt-3.5-turbo\")\ndf = dd.read_csv(\"products.csv\")\n\n# 使用Map创建新列\nmapped = dt.map(\n    prompt=\"从产品描述和名称中提取类别\",\n    output_fields=[\"Category\", \"Subcategory\"],\n    input_fields=[\"Description\", \"Name\"]\n)(llm, df)\n\n# 使用Filter过滤数据\nfiltered = dt.filter(\n    prompt=\"仅保留电子产品\",\n    input_fields=[\"Name\"]\n)(llm, mapped)\n\n# 保存结果\nresult = dt.finalize(filtered)\nresult.compute().to_csv(\"electronics_products.csv\")\n```\n\n### 7.3 Agent使用示例\n\n```python\nimport datatune as dt\nfrom datatune.llm.llm import OpenAI\n\nllm = OpenAI(model_name=\"gpt-3.5-turbo\")\nagent = dt.Agent(llm)\n\ndf = agent.do(\"添加ProfitMargin列并仅保留非洲组织\", df)\nresult = dt.finalize(df)\n```\n\nAgent会自动：\n- 确定使用哪些操作（map、filter等）\n- 链接多个转换步骤\n- 处理复杂的多步骤任务\n- 生成并执行Python代码\n\n资料来源：[README.md:1-100]()\n\n## 系统提示词设计\n\nAgent的系统提示词定义了可用的工具和上下文：\n\n资料来源：[datatune/agent/__init__.py:1-50]()\n\n**可用的Python库：**\n- pandas\n- numpy\n- dask\n\n**可用的数据处理原语：**\n- Map：通过LLM提示词从现有数据创建新列\n- Filter：通过LLM提示词根据条件移除行\n\n## 错误处理机制\n\n### 9.1 执行错误捕获\n\nAgent实现了完善的错误处理机制：\n\n```mermaid\ngraph TD\n    A[执行步骤] --> B{是否成功?}\n    B -->|是| C[记录日志]\n    B -->|否| D[捕获异常]\n    D --> E[格式化错误信息]\n    E --> F[返回错误和步骤号]\n```\n\n资料来源：[datatune/agent/agent.py:80-120]()\n\n### 9.2 速率限制警告\n\n当使用的模型未配置速率限制时，系统会发出警告：\n\n```python\nif \"rpm\" not in kwargs:\n    logger.warning(f\"REQUESTS-PER-MINUTE limits for model '{model_name}' not found.\")\nif \"tpm\" not in kwargs:\n    logger.warning(f\"TOKENS-PER-MINUTE limits for model '{model_name}' not found.\")\n```\n\n资料来源：[datatune/llm/llm.py:65-80]()\n\n## 总结\n\nDatatune是一个功能强大的LLM驱动数据转换框架，它：\n\n1. **简化数据处理**：通过自然语言描述替代复杂的代码编写\n2. **支持多种后端**：Dask用于大规模分布式计算，Ibis支持多种SQL数据库\n3. **集成多LLM**：OpenAI、Ollama、Azure、Mistral等主流提供商\n4. **智能去重**：基于嵌入向量的高效相似度检测\n5. **Agent自动化**：自动规划并执行复杂的数据转换流程\n\n该框架适用于需要对结构化数据进行转换、清洗和增强的各种场景，特别是当业务逻辑难以用传统编程表达时，Datatune能够显著提升开发效率。\n\n---\n\n<a id='quickstart'></a>\n\n## 快速开始\n\n### 相关页面\n\n相关主题：[Map操作](#map-operation), [Filter操作](#filter-operation), [Agent系统](#agent-system)\n\n<details>\n<summary>相关源码文件</summary>\n\n以下源码文件用于生成本页说明：\n\n- [README.md](https://github.com/vitalops/datatune/blob/main/README.md)\n- [datatune/llm/llm.py](https://github.com/vitalops/datatune/blob/main/datatune/llm/llm.py)\n- [datatune/agent/agent.py](https://github.com/vitalops/datatune/blob/main/datatune/agent/agent.py)\n- [datatune/agent/__init__.py](https://github.com/vitalops/datatune/blob/main/datatune/agent/__init__.py)\n- [datatune/core/dask/filter_dask.py](https://github.com/vitalops/datatune/blob/main/datatune/core/dask/filter_dask.py)\n- [datatune/core/dask/map_dask.py](https://github.com/vitalops/datatune/blob/main/datatune/core/dask/map_dask.py)\n- [datatune/llm/model_rate_limits.py](https://github.com/vitalops/datatune/blob/main/datatune/llm/model_rate_limits.py)\n</details>\n\n# 快速开始\n\n## 概述\n\ndatatune 是一个使用 LLM（大语言模型）进行数据转换和处理的 Python 库。它允许用户通过自然语言描述来执行复杂的数据操作任务，如数据映射、过滤和去重。快速开始指南旨在帮助用户在最短时间内上手 datatune，掌握其核心功能并完成基础的数据处理流程。\n\n该库支持多种 LLM 后端，包括 OpenAI、Ollama、Azure、Mistral、Huggingface 和 VLLM，同时兼容 Dask 和 Ibis（支持 DuckDB、PostgreSQL、BigQuery 等）数据源。资料来源：[README.md](https://github.com/vitalops/datatune/blob/main/README.md)\n\n## 环境准备\n\n### 安装方式\n\ndatatune 可通过 pip 直接安装：\n\n```bash\npip install datatune\n```\n\n资料来源：[README.md](https://github.com/vitalops/datatune/blob/main/README.md)\n\n### LLM 配置\n\ndatatune 支持多种 LLM 提供商，需根据使用的模型进行相应配置：\n\n| LLM 类型 | 初始化方式 | 默认模型 | 必需参数 |\n|---------|-----------|---------|---------|\n| OpenAI | `OpenAI(model_name=\"gpt-3.5-turbo\")` | gpt-3.5-turbo | api_key（可选） |\n| Ollama | `Ollama()` | gemma3:4b | api_base（默认 localhost:11434） |\n| Azure | `Azure(model_name=\"...\")` | - | api_key, api_base, api_version |\n| Mistral | `Mistral(model_name=\"mistral/mistral-tiny\")` | mistral/mistral-tiny | api_key |\n| Huggingface | `Huggingface(model_name=\"...\")` | - | api_key |\n| VLLM | `VLLM(model_name=\"...\")` | - | api_base |\n\n资料来源：[datatune/llm/llm.py](https://github.com/vitalops/datatune/blob/main/datatune/llm/llm.py)\n\n## 核心工作流程\n\ndatatune 的数据处理遵循标准的三步流程：加载数据、执行转换、最终化结果。以下是完整的工作流程图：\n\n```mermaid\ngraph TD\n    A[读取数据源] --> B[map 或 filter 转换]\n    B --> C{datatune.finalize]\n    C --> D[执行 compute]\n    D --> E[保存结果]\n    \n    F[LLM 实例] --> B\n```\n\n## 数据加载\n\n### Dask 数据源\n\n使用 Dask 读取 CSV 文件：\n\n```python\nimport dask.dataframe as dd\n\ndf = dd.read_csv(\"products.csv\")\n```\n\n### Ibis 数据源\n\n使用 Ibis 连接 DuckDB 或其他数据库：\n\n```python\nimport ibis\n\ncon = ibis.duckdb.connect(\"data.duckdb\")\ntable = con.table(\"my_table\")\n```\n\n资料来源：[README.md](https://github.com/vitalops/datatune/blob/main/README.md)\n\n## map 操作\n\n`dt.map()` 用于从现有列生成新的列，通过自然语言描述转换逻辑。\n\n```python\nimport datatune as dt\n\nmapped = dt.map(\n    prompt=\"Extract categories from the description and name of product.\",\n    output_fields=[\"Category\", \"Subcategory\"],\n    input_fields=[\"Description\", \"Name\"]\n)(llm, df)\n```\n\n### 参数说明\n\n| 参数 | 类型 | 说明 |\n|-----|------|-----|\n| prompt | str | 自然语言描述转换规则 |\n| output_fields | List[str] | 输出新列名列表 |\n| input_fields | List[str] | 输入列名列表 |\n\n资料来源：[README.md](https://github.com/vitalops/datatune/blob/main/README.md)\n\n### 实现原理\n\nmap 操作内部通过 LLM 调用处理数据，将每行数据序列化为字典格式，附加到提示词中发送给 LLM，LLM 返回包含新字段的字典。实现文件位于 `datatune/core/dask/map_dask.py` 和 `datatune/core/ibis/map_ibis.py`。\n\nmap_dask.py 中的提示词格式如下：\n\n```python\nsuffix = (\n    f\"{os.linesep}{os.linesep}\"\n    \"Your response MUST be the entire input record as a valid Python dictionary in the format\"\n    \"'index=<row_index>|{key1: value1, key2: value2, ...}'  with added keys of expected new fields if any.\"\n)\n```\n\n资料来源：[datatune/core/dask/map_dask.py](https://github.com/vitalops/datatune/blob/main/datatune/core/dask/map_dask.py)\n\n## filter 操作\n\n`dt.filter()` 用于根据条件过滤数据行。\n\n```python\nfiltered = dt.filter(\n    prompt=\"Keep only electronics products\",\n    input_fields=[\"Name\"]\n)(llm, mapped)\n```\n\n### 参数说明\n\n| 参数 | 类型 | 说明 |\n|-----|------|-----|\n| prompt | str | 自然语言描述过滤条件 |\n| input_fields | List[str] | 输入列名列表 |\n\n资料来源：[README.md](https://github.com/vitalops/datatune/blob/main/README.md)\n\n### 实现原理\n\nfilter 操作使用特殊格式让 LLM 判断每行是否应保留。返回格式包含 `__filter__` 键，值为 `True` 表示保留，`False` 表示移除。\n\n```python\nsuffix = (\n    f\"{os.linesep}{os.linesep}\"\n    \"DECISION:Your response MUST be the entire input record as a Python dictionary...\"\n    \"with added key called '__filter__' with value either True to KEEP the record or False to REMOVE it.\"\n)\n```\n\n资料来源：[datatune/core/dask/filter_dask.py](https://github.com/vitalops/datatune/blob/main/datatune/core/dask/filter_dask.py)\n\n## 最终化与保存结果\n\n使用 `dt.finalize()` 完成所有计算并将结果转换为标准 DataFrame：\n\n```python\nresult = dt.finalize(filtered)\nresult.compute().to_csv(\"electronics_products.csv\")\n```\n\n`finalize()` 函数负责触发 Dask 计算图的实际执行，将延迟操作转换为最终结果。资料来源：[README.md](https://github.com/vitalops/datatune/blob/main/README.md)\n\n## Agent 模式\n\n对于复杂的、多步骤的数据处理任务，可使用 `dt.Agent` 让 AI 自动规划转换步骤：\n\n```python\nimport datatune as dt\nfrom datatune.llm.llm import OpenAI\n\nllm = OpenAI(model_name=\"gpt-3.5-turbo\")\nagent = dt.Agent(llm)\n\ndf = dd.read_csv(\"data.csv\")\n\n# 描述复杂需求，Agent 自动规划执行步骤\ndf = agent.do(\"Add ProfitMargin column and keep only African organizations\", df)\nresult = dt.finalize(df)\n```\n\n### Agent 自动完成的工作\n\n| 功能 | 说明 |\n|-----|------|\n| 操作类型判断 | 自动决定使用 map、filter 还是其他操作 |\n| 步骤链式执行 | 自动串联多个转换步骤 |\n| 复杂任务处理 | 支持从单一提示词执行多步操作 |\n| 代码生成 | 生成 Python 代码和行级原语操作 |\n\n资料来源：[README.md](https://github.com/vitalops/datatune/blob/main/README.md)\n\n### Agent 系统提示词\n\nAgent 基于以下系统提示词工作：\n\n```\nYou are Datatune Agent, a powerful assistant designed to help users with data processing tasks.\nYou are capable of generating python code to perform various operations on data. Apart from python builtins, you have the following libraries avaiable in your runtime:\n- pandas\n- numpy\n- dask\n```\n\n资料来源：[datatune/agent/__init__.py](https://github.com/vitalops/datatune/blob/main/datatune/agent/__init__.py)\n\n## 模型速率限制\n\ndatatune 内置了常见 OpenAI 模型的速率限制配置，确保 API 调用不会超出限制：\n\n| 模型 | TPM（每分钟令牌数） | RPM（每分钟请求数） |\n|-----|---------------------|---------------------|\n| gpt-3.5-turbo | 200,000 | 500 |\n| gpt-4 | 10,000 | 500 |\n| gpt-4-turbo | 30,000 | 500 |\n| gpt-4.1 | 30,000 | 500 |\n| gpt-4.1-mini | 200,000 | 500 |\n| gpt-4.1-nano | 200,000 | 500 |\n| gpt-4o | 30,000 | 500 |\n\n资料来源：[datatune/llm/model_rate_limits.py](https://github.com/vitalops/datatune/blob/main/datatune/llm/model_rate_limits.py)\n\n## 完整示例\n\n以下是一个完整的快速开始示例，整合所有核心功能：\n\n```python\nimport datatune as dt\nfrom datatune.llm.llm import OpenAI\nimport dask.dataframe as dd\n\n# 1. 初始化 LLM\nllm = OpenAI(model_name=\"gpt-3.5-turbo\")\n\n# 2. 加载数据\ndf = dd.read_csv(\"products.csv\")\n\n# 3. 使用 map 提取类别\nmapped = dt.map(\n    prompt=\"Extract categories from the description and name of product.\",\n    output_fields=[\"Category\", \"Subcategory\"],\n    input_fields=[\"Description\", \"Name\"]\n)(llm, df)\n\n# 4. 使用 filter 筛选电子产品\nfiltered = dt.filter(\n    prompt=\"Keep only electronics products\",\n    input_fields=[\"Name\"]\n)(llm, mapped)\n\n# 5. 最终化并保存\nresult = dt.finalize(filtered)\nresult.compute().to_csv(\"electronics_products.csv\")\n```\n\n## 后续步骤\n\n- 查看完整文档：https://docs.datatune.ai/\n- 参考示例代码：https://github.com/vitalops/datatune/tree/main/examples\n- 加入社区 Discord：https://discord.gg/3RKA5AryQX\n\n---\n\n<a id='architecture'></a>\n\n## 系统架构\n\n### 相关页面\n\n相关主题：[Agent系统](#agent-system), [LLM集成](#llm-integration), [数据源支持](#datasource)\n\n<details>\n<summary>相关源码文件</summary>\n\n以下源码文件用于生成本页说明：\n\n- [datatune/llm/llm.py](https://github.com/vitalops/datatune/blob/main/datatune/llm/llm.py)\n- [datatune/agent/agent.py](https://github.com/vitalops/datatune/blob/main/datatune/agent/agent.py)\n- [datatune/core/dask/map_dask.py](https://github.com/vitalops/datatune/blob/main/datatune/core/dask/map_dask.py)\n- [datatune/core/dask/filter_dask.py](https://github.com/vitalops/datatune/blob/main/datatune/core/dask/filter_dask.py)\n- [datatune/core/deduplication.py](https://github.com/vitalops/datatune/blob/main/datatune/core/deduplication.py)\n- [datatune/__init__.py](https://github.com/vitalops/datatune/blob/main/datatune/__init__.py)\n</details>\n\n# 系统架构\n\n## 概述\n\nDatatune 是一个基于大语言模型（LLM）的数据处理框架，其核心设计目标是通过自然语言描述实现对数据的转换、过滤和去重操作。系统采用模块化架构，将 LLM 交互、数据处理后端和智能代理三个层面解耦，支持 Dask 和 Ibis 两种数据处理后端，兼容 DuckDB、PostgreSQL、BigQuery 等多种数据源。\n\n资料来源：[datatune/__init__.py:1-7]()\n\n## 架构分层\n\nDatatune 采用三层架构设计，从下至上依次为：\n\n```mermaid\ngraph TD\n    subgraph \"表现层\"\n        Agent\n    end\n    \n    subgraph \"核心操作层\"\n        Map\n        Filter\n        Reduce\n    end\n    \n    subgraph \"后端抽象层\"\n        Dask后端\n        Ibis后端\n    end\n    \n    subgraph \"LLM交互层\"\n        OpenAI\n        Ollama\n        VLLM\n        Azure\n    end\n    \n    Agent --> Map\n    Agent --> Filter\n    Map --> Dask后端\n    Map --> Ibis后端\n    Filter --> Dask后端\n    Filter --> Ibis后端\n    OpenAI --> Agent\n    Ollama --> Agent\n    VLLM --> Agent\n    Azure --> Agent\n```\n\n### 各层职责\n\n| 层级 | 组件 | 职责 |\n|------|------|------|\n| 表现层 | Agent | 理解用户意图，规划执行步骤，生成并执行代码 |\n| 核心操作层 | Map/Filter/Reduce | 提供声明式数据转换接口 |\n| 后端抽象层 | Dask/Ibis | 实现分布式数据处理逻辑 |\n| LLM交互层 | 各LLM实现类 | 封装与不同语言模型的通信协议 |\n\n资料来源：[datatune/agent/agent.py:1-15]()\n\n## LLM 交互层\n\n### 抽象基类设计\n\n`LLM` 是所有 LLM 提供者的抽象基类，通过 `litellm` 库实现统一的模型调用接口：\n\n```python\nclass LLM:\n    def __init__(self, model_name: str, **kwargs) -> None:\n        self.model_name = model_name  # 格式: \"provider/model\"\n        self._base_model_name = model_name.split(\"/\", 1)[1]\n```\n\n资料来源：[datatune/llm/llm.py:17-24]()\n\n### 支持的 LLM 提供者\n\n| 提供者 | 类名 | 默认模型 | 特点 |\n|--------|------|----------|------|\n| OpenAI | `OpenAI` | gpt-3.5-turbo | 云端 API |\n| Ollama | `Ollama` | gemma3:4b | 本地部署 |\n| VLLM | `VLLM` | 需指定 | 高性能推理 |\n| Azure | `Azure` | 需指定 | 企业级部署 |\n\n资料来源：[datatune/llm/llm.py:65-108]()\n\n### 模型速率限制\n\n系统内置了主流模型的速率限制配置，包含每分钟请求数（RPM）和每分钟令牌数（TPM）：\n\n```python\nmodel_rate_limits = {\n    \"gpt-3.5-turbo\": {\"tpm\": 200_000, \"rpm\": 500},\n    \"gpt-4\": {\"tpm\": 10_000, \"rpm\": 500},\n    \"gpt-4o\": {\"tpm\": 30_000, \"rpm\": 500},\n}\n```\n\n资料来源：[datatune/llm/model_rate_limits.py:1-20]()\n\n## 核心操作层\n\n### Map 操作\n\nMap 操作通过 LLM 将现有列转换为新列，实现数据转换功能：\n\n```mermaid\ngraph LR\n    A[输入DataFrame] --> B[序列化输入列]\n    B --> C[LLM批量推理]\n    C --> D[解析输出字典]\n    D --> E[写入新列]\n    E --> F[输出DataFrame]\n```\n\n关键流程包括：\n1. 将输入列序列化为字符串格式\n2. 调用 LLM 批量处理，根据 prompt 生成映射结果\n3. 解析 LLM 返回的 Python 字典格式输出\n4. 将结果写入新生成的列\n\n资料来源：[datatune/core/dask/map_dask.py:1-50]()\n\n### Filter 操作\n\nFilter 操作根据 LLM 生成的决策条件过滤数据行：\n\n```mermaid\ngraph LR\n    A[输入DataFrame] --> B[序列化输入列]\n    B --> C[LLM决策判断]\n    C --> D[解析__filter__字段]\n    D --> E[布尔索引过滤]\n    E --> F[输出DataFrame]\n```\n\nLLM 响应格式要求包含 `__filter__` 字段，值为 `True` 表示保留，`False` 表示移除：\n\n```\nindex=0|{key1: value1, key2: value2, '__filter__': True}<endofrow>\n```\n\n资料来源：[datatune/core/dask/filter_dask.py:1-60]()\n\n### 语义去重\n\n`SemanticDeduplicator` 使用嵌入向量和 FAISS 索引实现语义级别的去重：\n\n```mermaid\ngraph TD\n    A[原始数据] --> B[嵌入生成]\n    B --> C[FAISS HNSW索引]\n    C --> D[流式聚类]\n    D --> E[重复组识别]\n    E --> F[规范ID映射]\n```\n\n去重器支持以下参数：\n\n| 参数 | 说明 | 默认值 |\n|------|------|--------|\n| embedding_model | 嵌入模型名称 | text-embedding-3-small |\n| sim_threshold | 相似度阈值 | 0.90 |\n| top_k | 最近邻数量 | 50 |\n| hnsw_m | HNSW 索引参数 | 32 |\n| ef_search | 搜索精度参数 | 64 |\n\n资料来源：[datatune/core/deduplication.py:1-80]()\n\n## Agent 代理层\n\n### Agent 执行流程\n\nAgent 是系统的智能编排层，能够自动分析用户需求并生成执行计划：\n\n```mermaid\ngraph TD\n    A[用户自然语言描述] --> B[LLM生成执行计划]\n    B --> C{计划步骤循环}\n    C -->|步骤类型为dask| D[执行Dask操作模板]\n    C -->|步骤类型为primitive| E[执行Primitive操作]\n    D --> F[记录步骤日志]\n    E --> F\n    F --> G{还有更多步骤?}\n    G -->|是| C\n    G -->|否| H[计算并返回结果]\n```\n\n### 执行计划结构\n\nAgent 生成的执行计划是一个 JSON 数组，每个步骤包含以下字段：\n\n| 字段 | 类型 | 说明 |\n|------|------|------|\n| type | string | \"dask\" 或 \"primitive\" |\n| operation | string | 操作名称 |\n| params | dict | Dask 模板参数 |\n| subprompt | string | Primitive 操作的 LLM 提示词 |\n| input_fields | list | 输入列名 |\n| output_fields | list | 输出列名（仅 Map） |\n\n资料来源：[datatune/agent/agent.py:80-120]()\n\n### 操作模板\n\nAgent 支持两种类型的操作模板：\n\n#### Dask 操作模板\n\n用于直接生成 Dask DataFrame 操作代码：\n\n```python\nTEMPLATE = {\n    \"dask\": {\n        \"add_column\": \"df = df.assign({new_column}=...)\",\n        \"group_by\": \"df = df.groupby(...).agg(...)\",\n        \"rename_columns\": \"df = df.rename(columns={mapping})\",\n    }\n}\n```\n\n#### Primitive 操作模板\n\n用于通过 LLM 生成逐行转换逻辑：\n\n```python\nTEMPLATE = {\n    \"primitive\": {\n        \"Map\": \"df = dt.Map(subprompt=...)\",\n        \"Filter\": \"df = dt.Filter(subprompt=...)\",\n    }\n}\n```\n\n资料来源：[datatune/agent/agent.py:25-50]()\n\n## 数据流处理\n\n### Dask 分区处理\n\n系统使用 `map_partitions` 实现分布式数据处理，保证每个分区独立执行 LLM 操作：\n\n```python\ncode_lines += [\n    f\"df = df.map_partitions(log_primitive, {start_msg!r}, meta=df._meta)\",\n    \"step_num += 1\",\n    template,\n    f\"df = df.map_partitions(log_primitive, {end_msg!r}, meta=df._meta)\"\n]\n```\n\n资料来源：[datatune/agent/agent.py:55-75]()\n\n### 重复记录处理\n\n在 Map 和 Filter 操作中，系统采用规范 ID 映射策略处理重复记录：\n\n```python\ndup_to_canon = {\n    dup: c[\"canonical_id\"]\n    for c in clusters\n    for dup in c[\"duplicate_ids\"]\n}\n\ncanonical_idx = input_series.index.difference(dup_to_canon.keys())\ncanonical_input = input_series.loc[canonical_idx]\nllm_out = llm(canonical_input, ...)\n\n# 将结果回填到重复记录\nfor dup, canon in dup_to_canon.items():\n    df.loc[dup, llm_output_column] = df.loc[canon, llm_output_column]\n```\n\n资料来源：[datatune/core/dask/map_dask.py:30-55]()\n\n## 公共 API\n\n系统通过 `datatune/__init__.py` 导出以下核心组件：\n\n```python\nfrom datatune.agent.agent import Agent\nfrom datatune.core.filter import filter\nfrom datatune.core.map import map\nfrom datatune.core.dask.op import finalize\nfrom datatune.core.deduplication import SemanticDeduplicator\nfrom datatune.core.reduce import reduce\n\n__all__ = [\"map\", \"filter\", \"finalize\", \"Agent\", \"reduce\"]\n```\n\n资料来源：[datatune/__init__.py:1-10]()\n\n## 错误处理机制\n\n### 步骤级错误追踪\n\nAgent 在执行计划时维护步骤计数器，失败时返回错误信息和失败步骤编号：\n\n```python\ndef _execute_plan(self, plan: List[Dict]):\n    self.runtime.update({\"step_num\": 0, \"plan\": plan})\n    for i, step in enumerate(plan, start=1):\n        try:\n            # 执行步骤...\n            self.runtime[\"step_num\"] = i\n        except Exception as e:\n            return str(e), i - 1\n```\n\n资料来源：[datatune/agent/agent.py:45-70]()\n\n### 输出解析容错\n\nLLM 输出解析使用 `ast.literal_eval` 进行安全评估，解析失败时返回空字典：\n\n```python\ndef safe_parse(row):\n    try:\n        return ast.literal_eval(row)\n    except Exception:\n        return {}\n```\n\n资料来源：[datatune/core/deduplication.py:60-65]()\n\n## 总结\n\nDatatune 的系统架构体现了以下设计原则：\n\n1. **层次化解耦**：LLM 交互、数据处理和用户接口三层分离，便于维护和扩展\n2. **多后端支持**：通过 Dask 和 Ibis 抽象，支持从单机到分布式多种部署场景\n3. **智能编排**：Agent 层自动规划执行路径，降低用户使用门槛\n4. **容错设计**：完善的错误处理和日志记录机制，便于问题诊断\n5. **语义去重**：基于嵌入向量的语义匹配，实现精准的数据去重\n\n---\n\n<a id='datamanagement'></a>\n\n## 数据管理与数据流\n\n### 相关页面\n\n相关主题：[数据源支持](#datasource), [Map操作](#map-operation), [Filter操作](#filter-operation)\n\n<details>\n<summary>相关源码文件</summary>\n\n以下源码文件用于生成本页说明：\n\n- [datatune/core/dask/map_dask.py](https://github.com/vitalops/datatune/blob/main/datatune/core/dask/map_dask.py)\n- [datatune/core/dask/filter_dask.py](https://github.com/vitalops/datatune/blob/main/datatune/core/dask/filter_dask.py)\n- [datatune/core/ibis/map_ibis.py](https://github.com/vitalops/datatune/blob/main/datatune/core/ibis/map_ibis.py)\n- [datatune/core/ibis/filter_ibis.py](https://github.com/vitalops/datatune/blob/main/datatune/core/ibis/filter_ibis.py)\n- [datatune/core/deduplication.py](https://github.com/vitalops/datatune/blob/main/datatune/core/deduplication.py)\n- [datatune/agent/agent.py](https://github.com/vitalops/datatune/blob/main/datatune/agent/agent.py)\n- [datatune/core/reduce.py](https://github.com/vitalops/datatune/blob/main/datatune/core/reduce.py)\n- [datatune/llm/llm.py](https://github.com/vitalops/datatune/blob/main/datatune/llm/llm.py)\n</details>\n\n# 数据管理与数据流\n\n## 概述\n\nDatatune 是一个基于 LLM 的数据处理库，通过自然语言提示词实现数据的映射（Map）、过滤（Filter）和去重（Deduplication）操作。系统支持多种后端数据源，包括 Dask DataFrame 和 Ibis（支持 DuckDB、PostgreSQL、BigQuery 等），并通过统一的 API 接口实现数据流的透明处理。\n\n核心设计理念是将复杂的数据转换逻辑封装为可组合的操作符，用户只需通过自然语言描述期望的转换结果，系统即可自动生成并执行相应的处理流程。\n\n## 架构概览\n\n```mermaid\ngraph TD\n    subgraph \"数据输入层\"\n        CSV[CSV 文件]\n        SQLDB[(SQL 数据库)]\n        DuckDB[(DuckDB)]\n    end\n    \n    subgraph \"抽象接口层\"\n        DD[Dask DataFrame]\n        IB[Ibis Table]\n    end\n    \n    subgraph \"核心操作层\"\n        MAP[Map 操作]\n        FILTER[Filter 操作]\n        DEDUP[去重操作]\n        REDUCE[Reduce 操作]\n    end\n    \n    subgraph \"LLM 服务层\"\n        OPENAI[OpenAI]\n        OLLAMA[Ollama]\n        AZURE[Azure]\n        VLLM[VLLM]\n    end\n    \n    CSV --> DD\n    SQLDB --> IB\n    DuckDB --> IB\n    \n    DD --> MAP\n    DD --> FILTER\n    DD --> DEDUP\n    IB --> MAP\n    IB --> FILTER\n    \n    MAP --> LLM\n    FILTER --> LLM\n    \n    LLM --> OPENAI\n    LLM --> OLLAMA\n    LLM --> AZURE\n    LLM --> VLLM\n```\n\n## 数据源支持\n\nDatatune 通过抽象数据类型检测机制自动选择合适的后端实现。\n\n### 支持的数据源\n\n| 数据源类型 | 后端实现 | 导入方式 |\n|-----------|---------|---------|\n| CSV/Parquet | Dask | `dask.dataframe as dd` |\n| DuckDB | Ibis | `ibis.duckdb.connect()` |\n| PostgreSQL | Ibis | `ibis.postgres.connect()` |\n| BigQuery | Ibis | `ibis.bigquery.connect()` |\n\n### 数据类型检测\n\n数据类型检测通过 `__init__.py` 中的内部函数实现：\n\n```python\ndef _is_ibis_table(obj):\n    import ibis\n    return isinstance(obj, ibis.Table)\n\ndef _is_dask_df(obj):\n    import dask.dataframe as dd\n    return isinstance(obj, dd.DataFrame)\n```\n\n资料来源：[datatune/core/map.py:1-16](https://github.com/vitalops/datatune/blob/main/datatune/core/map.py)\n\n## Map 操作数据流\n\nMap 操作是 Datatune 的核心功能之一，用于根据自然语言提示词生成新的数据列。\n\n### 数据处理流程\n\n```mermaid\ngraph LR\n    A[输入 DataFrame] --> B[序列化输入列]\n    B --> C[去重预处理]\n    C --> D[批量调用 LLM]\n    D --> E[解析 LLM 输出]\n    E --> F[合并去重结果]\n    F --> G[输出包含新列的 DataFrame]\n```\n\n### Dask 后端 Map 实现\n\nDask 后端的 Map 操作通过 `_map_dask` 函数实现，采用延迟计算和分区处理策略：\n\n```python\ndef _map_dask(prompt, output_fields, input_fields=None, clusters=None):\n    # 1. 序列化输入列为 JSON 字符串\n    serialized_input_column = \"serialized_input\"\n    df[serialized_input_column] = df[input_fields].apply(\n        lambda x: json.dumps(x.to_dict()), axis=1, meta=(serialized_input_column, str)\n    )\n    \n    # 2. 构建 LLM 调用前缀和后缀\n    prefix = \"...\"\n    suffix = \"Your response MUST be the entire input record...\"\n    \n    # 3. 处理重复数据的预处理\n    dup_to_canon = {\n        dup: c[\"canonical_id\"]\n        for c in clusters\n        for dup in c[\"duplicate_ids\"]\n    }\n    canonical_idx = input_series.index.difference(dup_to_canon.keys())\n    canonical_input = input_series.loc[canonical_idx]\n    \n    # 4. 批量调用 LLM\n    llm_out = llm(canonical_input, prefix, prompt, suffix, optimized=True)\n    \n    # 5. 将 LLM 结果写回 DataFrame\n    df.loc[canonical_idx, llm_output_column] = llm_out\n    \n    # 6. 复制去重结果到重复行\n    for dup, canon in dup_to_canon.items():\n        df.loc[dup, llm_output_column] = df.loc[canon, llm_output_column]\n```\n\n资料来源：[datatune/core/dask/map_dask.py:1-45](https://github.com/vitalops/datatune/blob/main/datatune/core/dask/map_dask.py)\n\n### Ibis 后端 Map 实现\n\nIbis 后端采用不同的实现方式，使用行号索引和内存表进行数据处理：\n\n```python\ndef _map_ibis(table, llm, input_col, output_col, prompt, expected_new_fields):\n    # 添加行号索引\n    indexed_table = table.mutate(_ROW_ID_=ibis.row_number().cast(\"int64\"))\n    \n    # 将数据拉到本地执行\n    local_data = indexed_table.select(\"_ROW_ID_\", input_col).execute()\n    input_list = local_data[input_col].tolist()\n    \n    # 调用 LLM\n    raw_results = llm(input_list, prefix, mapping_prompt, suffix, optimized=True)\n    \n    # 解析结果并处理异常\n    processed_results = []\n    for res in raw_results:\n        try:\n            py_dict = ast.literal_eval(str(res).strip())\n            processed_results.append(json.dumps(py_dict))\n        except Exception:\n            processed_results.append(\"{}\")\n    \n    # 创建内存表并关联\n    mapping_df = pd.DataFrame({\n        \"_ROW_ID_\": local_data[\"_ROW_ID_\"].values, \n        output_col: processed_results\n    })\n    mapping_table = ibis.memtable(mapping_df)\n```\n\n资料来源：[datatune/core/ibis/map_ibis.py:1-50](https://github.com/vitalops/datatune/blob/main/datatune/core/ibis/map_ibis.py)\n\n### LLM 输出格式规范\n\nMap 操作要求 LLM 返回特定格式的输出：\n\n```\nindex=<row_index>|{key1: value1, key2: value2, ...}\n```\n\n- 必须以 `index=<row_index>|` 开头\n- 主体必须是有效的 Python 字典格式\n- 新增字段必须使用双引号\n- 缺失值使用 `None` 表示\n\n资料来源：[datatune/core/dask/map_dask.py:15-25](https://github.com/vitalops/datatune/blob/main/datatune/core/dask/map_dask.py)\n\n## Filter 操作数据流\n\nFilter 操作根据自然语言条件过滤数据行。\n\n### 过滤处理流程\n\n```mermaid\ngraph LR\n    A[输入 DataFrame] --> B[序列化数据行]\n    B --> C[构建过滤提示词]\n    C --> D[批量调用 LLM]\n    D --> E[解析过滤决策]\n    E --> F[应用过滤掩码]\n    F --> G[输出过滤后的 DataFrame]\n```\n\n### Dask 后端 Filter 实现\n\n```python\ndef _filter_dask(df, llm, input_fields, prompt, clusters=None):\n    # 序列化输入数据\n    serialized_input_column = \"serialized_input\"\n    df[serialized_input_column] = df[input_fields].apply(\n        lambda x: json.dumps(x.to_dict()), axis=1, meta=(serialized_input_column, str)\n    )\n    \n    # 构建过滤提示词\n    suffix = (\n        f\"{os.linesep}{os.linesep}\"\n        \"DECISION:Your response MUST be the entire input record...\"\n        \"with added key called '__filter__' with value either True to KEEP \"\n        \"the record or False to REMOVE it.\"\n    )\n    \n    # 去重处理\n    dup_to_canon = {\n        dup: c[\"canonical_id\"]\n        for c in clusters\n        for dup in c[\"duplicate_ids\"]\n    }\n    canonical_idx = input_series.index.difference(dup_to_canon.keys())\n    canonical_input = input_series.loc[canonical_idx]\n    \n    # 调用 LLM 获取过滤决策\n    llm_out = llm(canonical_input, prefix, prompt, suffix, optimized=True)\n    \n    # 合并去重结果\n    for dup, canon in dup_to_canon.items():\n        df.loc[dup, llm_output_column] = df.loc[canon, llm_output_column]\n```\n\n资料来源：[datatune/core/dask/filter_dask.py:1-40](https://github.com/vitalops/datatune/blob/main/datatune/core/dask/filter_dask.py)\n\n### LLM 过滤决策格式\n\nFilter 操作要求 LLM 返回带有 `__filter__` 键的字典：\n\n```\nindex=<row_index>|{key1: value1, key2: value2, ..., '__filter__': True}\n```\n\n- `__filter__: True` 表示保留该行\n- `__filter__: False` 表示移除该行\n\n资料来源：[datatune/core/dask/filter_dask.py:18-24](https://github.com/vitalops/datatune/blob/main/datatune/core/dask/filter_dask.py)\n\n## 去重机制\n\nDatatune 实现了基于语义相似度的去重功能，用于优化 LLM 调用并保持数据一致性。\n\n### 去重处理架构\n\n```mermaid\ngraph TD\n    A[输入分区数据] --> B[Embedding 向量化]\n    B --> C[构建 FAISS HNSW 索引]\n    C --> D[相似度搜索]\n    D --> E[聚类重复数据]\n    E --> F[合并重复行结果]\n```\n\n### 去重处理流程\n\n```python\ndef _embed_and_write_partition(self, part, partition_id, output_dir):\n    # 解析行为字典\n    dicts = [safe_parse(row) for row in pdf]\n    \n    # 构建文本表示\n    texts = [\n        \", \".join(f\"{k}: {v}\" for k, v in d.items())\n        for d in dicts\n    ]\n    \n    # 批量生成 Embedding\n    for i in range(0, len(texts), 256):\n        batch = texts[i:i+256]\n        resp = embedding(model=self.embedding_model, input=batch)\n        embeddings.extend([item[\"embedding\"] for item in resp[\"data\"]])\n    \n    # 归一化并存储\n    X = np.asarray(embeddings, dtype=\"float32\")\n    faiss.normalize_L2(X)\n    np.save(f\"{output_dir}/embeddings_part_{partition_id}.npy\", X)\n```\n\n资料来源：[datatune/core/deduplication.py:1-60](https://github.com/vitalops/datatune/blob/main/datatune/core/deduplication.py)\n\n### 去重配置参数\n\n| 参数 | 默认值 | 说明 |\n|-----|-------|------|\n| `embedding_model` | `text-embedding-3-small` | 用于向量化的 Embedding 模型 |\n| `sim_threshold` | `0.90` | 相似度阈值，超过此值视为重复 |\n| `top_k` | `50` | 搜索时返回的最近邻数量 |\n| `hnsw_m` | `32` | HNSW 索引参数 M |\n| `ef_search` | `64` | HNSW 搜索参数 |\n\n资料来源：[datatune/core/deduplication.py:1-25](https://github.com/vitalops/datatune/blob/main/datatune/core/deduplication.py)\n\n## Reduce 操作\n\nReduce 操作通过注册-执行模式实现数据的聚合转换。\n\n### 架构设计\n\n```mermaid\ngraph LR\n    A[定义 Action] --> B[注册到全局表]\n    B --> C[调用 reduce 函数]\n    C --> D[查找并实例化 Action]\n    D --> E[执行数据处理]\n```\n\n### Action 注册机制\n\n```python\n_ACTIONS = {}\n\ndef register_action(name):\n    def decorator(cls):\n        _ACTIONS[name] = cls\n        return cls\n    return decorator\n\ndef get_action(name):\n    try:\n        return _ACTIONS[name]\n    except KeyError:\n        raise ValueError(f\"Unknown action: {name}\")\n\ndef reduce(df, *, action: str, **kwargs):\n    cls = get_action(action)\n    reducer = cls(**kwargs)   \n    return reducer(df)\n```\n\n资料来源：[datatune/core/reduce.py:1-25](https://github.com/vitalops/datatune/blob/main/datatune/core/reduce.py)\n\n## Agent 编排层\n\nAgent 是 Datatune 的智能编排组件，能够根据用户自然语言描述自动规划并执行数据处理流程。\n\n### Agent 工作流\n\n```mermaid\ngraph TD\n    A[用户描述任务] --> B[规划器分析意图]\n    B --> C{判断操作类型}\n    C -->|需要语义理解| D[使用 Primitive 操作]\n    C -->|纯数据转换| E[使用 Dask 操作]\n    D --> F[执行 Map/Filter]\n    E --> G[执行 add_column/group_by 等]\n    F --> H[合并结果]\n    G --> H\n    H --> I[返回处理后的 DataFrame]\n```\n\n### 计划执行流程\n\n```python\ndef _execute_plan(self, plan: List[Dict]):\n    self._set_df(self.df)\n    runtime.update({\"step_num\": 0, \"plan\": plan})\n    \n    for i, step in enumerate(plan, start=1):\n        step_type = step[\"type\"]\n        operation = step[\"operation\"]\n        params = step[\"params\"]\n        \n        if step_type == \"dask\":\n            template = self.TEMPLATE[\"dask\"][step[\"operation\"]].format(**params)\n            self.runtime.execute(template + \"\\n_ = df.head()\")\n        elif step_type == \"primitive\":\n            template = self.TEMPLATE[\"primitive\"][step[\"operation\"]].format(**params)\n            self.runtime.execute(template)\n```\n\n资料来源：[datatune/agent/agent.py:1-80](https://github.com/vitalops/datatune/blob/main/datatune/agent/agent.py)\n\n### 支持的操作类型\n\n#### Primitive 操作\n\n| 操作类型 | 说明 | 参数 |\n|---------|------|------|\n| `Map` | 从文本生成新列 | `subprompt`, `input_fields`, `output_fields` |\n| `Filter` | 根据条件过滤行 | `subprompt`, `input_fields` |\n\n#### Dask 操作\n\n| 操作类型 | 说明 | 参数 |\n|---------|------|------|\n| `add_column` | 从表达式添加列 | `new_column`, `expression` |\n| `group_by_agg` | 分组聚合 | `group_columns`, `aggregations` |\n| `rename_columns` | 重命名列 | `mapping` |\n| `astype_column` | 改变列类型 | `column`, `dtype` |\n\n资料来源：[datatune/agent/agent.py:50-100](https://github.com/vitalops/datatune/blob/main/datatune/agent/agent.py)\n\n## LLM 接口层\n\nLLM 模块提供了统一的接口，支持多种大语言模型服务。\n\n### 支持的 LLM 提供商\n\n| 提供商 | 类名 | 默认模型 | 配置参数 |\n|-------|------|---------|---------|\n| OpenAI | `OpenAI` | `gpt-3.5-turbo` | `api_key` |\n| Ollama | `Ollama` | `gemma3:4b` | `api_base` |\n| Azure | `Azure` | 自定义 | `api_key`, `api_base`, `api_version` |\n| VLLM | `VLLM` | 自定义 | `api_base`, `max_tokens` |\n\n### 批处理机制\n\nLLM 模块实现了智能批处理，根据输入长度动态分组请求以优化 API 调用：\n\n```python\n# token 计数估算\nprefix_suffix_tokens = token_counter(model_name, messages=message(\"\"))\ntotal_ntokens = prefix_suffix_tokens\n\nfor i, prompt in enumerate(input_rows):\n    # 动态批处理逻辑\n    # 当累计 token 接近限制时执行当前批次\n```\n\n资料来源：[datatune/llm/llm.py:1-60](https://github.com/vitalops/datatune/blob/main/datatune/llm/llm.py)\n\n## API 参考\n\n### 核心函数\n\n#### map 函数\n\n```python\ndef map(*, prompt, output_fields, input_fields=None, clusters=None):\n    def apply(llm, data):\n        if _is_dask_df(data):\n            from .dask.map_dask import _map_dask\n            return _map_dask(prompt=prompt, output_fields=output_fields, \n                           input_fields=input_fields, clusters=clusters)(llm, data)\n        elif _is_ibis_table(data):\n            from .ibis.map_ibis import _map_ibis\n            return _map_ibis(prompt=prompt, output_fields=output_fields,\n                            input_fields=input_fields)(llm, data)\n    return apply\n```\n\n资料来源：[datatune/core/map.py:20-38](https://github.com/vitalops/datatune/blob/main/datatune/core/map.py)\n\n#### filter 函数\n\n```python\ndef filter(*, prompt, input_fields=None, clusters=None):\n    def apply(llm, data):\n        if _is_dask_df(data):\n            from .dask.filter_dask import _filter_dask\n            return _filter_dask(prompt=prompt, input_fields=input_fields,\n                               clusters=clusters)(llm, data)\n        elif _is_ibis_table(data):\n            from .ibis.filter_ibis import _filter_ibis\n            return _filter_ibis(prompt=prompt, input_fields=input_fields)(llm, data)\n    return apply\n```\n\n资料来源：[datatune/core/filter.py:20-36](https://github.com/vitalops/datatune/blob/main/datatune/core/filter.py)\n\n### 模块导出\n\n```python\nfrom datatune import map, filter, finalize, Agent, reduce\n```\n\n资料来源：[datatune/__init__.py:1-8](https://github.com/vitalops/datatune/blob/main/datatune/__init__.py)\n\n## 完整使用示例\n\n```python\nimport datatune as dt\nfrom datatune.llm.llm import OpenAI\nimport dask.dataframe as dd\n\n# 初始化 LLM\nllm = OpenAI(model_name=\"gpt-3.5-turbo\")\n\n# 读取数据\ndf = dd.read_csv(\"products.csv\")\n\n# Map: 提取分类信息\nmapped = dt.map(\n    prompt=\"Extract categories from the description and name of product.\",\n    output_fields=[\"Category\", \"Subcategory\"],\n    input_fields=[\"Description\", \"Name\"]\n)(llm, df)\n\n# Filter: 保留电子产品\nfiltered = dt.filter(\n    prompt=\"Keep only electronics products\",\n    input_fields=[\"Name\"]\n)(llm, mapped)\n\n# 获取最终结果\nresult = dt.finalize(filtered)\nresult.compute().to_csv(\"electronics_products.csv\")\n\n---\n\n<a id='map-operation'></a>\n\n## Map操作\n\n### 相关页面\n\n相关主题：[Filter操作](#filter-operation), [Reduce操作](#reduce-operation)\n\n<details>\n<summary>相关源码文件</summary>\n\n以下源码文件用于生成本页说明：\n\n- [datatune/core/map.py](https://github.com/vitalops/datatune/blob/main/datatune/core/map.py)\n- [datatune/core/dask/map_dask.py](https://github.com/vitalops/datatune/blob/main/datatune/core/dask/map_dask.py)\n- [datatune/core/ibis/map_ibis.py](https://github.com/vitalops/datatune/blob/main/datatune/core/ibis/map_ibis.py)\n- [datatune/llm/llm.py](https://github.com/vitalops/datatune/blob/main/datatune/llm/llm.py)\n- [datatune/agent/agent.py](https://github.com/vitalops/datatune/blob/main/datatune/agent/agent.py)\n</details>\n\n# Map操作\n\n## 概述\n\nMap操作是datatune库的核心功能之一，它通过大型语言模型（LLM）实现对数据列的语义转换和增强。用户只需提供自然语言描述的转换规则，Map操作即可自动解析数据、调用LLM生成新字段，并返回增强后的数据集。\n\n该操作支持Dask和Ibis两种后端，能够处理分布式数据源（如CSV、数据库表等），同时内置去重优化机制，避免对语义相同的记录重复调用LLM API，从而显著降低使用成本。\n\n## 架构设计\n\n### 模块层次\n\n```\ndatatune.core.map (顶层接口)\n    ├── datatune.core.dask.map_dask (Dask后端实现)\n    └── datatune.core.ibis.map_ibis (Ibis后端实现)\n```\n\n### 执行流程\n\n```mermaid\ngraph TD\n    A[开始Map操作] --> B[序列化输入列]\n    B --> C{启用去重?}\n    C -->|是| D[构建重复记录映射]\n    C -->|否| E[跳过去重]\n    D --> F[提取唯一记录索引]\n    E --> G[调用LLM API]\n    F --> G\n    G --> H[解析LLM输出]\n    H --> I[填充结果到DataFrame]\n    I --> J[返回增强后的数据]\n    D --> K[复制结果到重复记录]\n    K --> J\n```\n\n## 核心实现\n\n### Dask后端 map_dask.py\n\n#### 去重优化机制\n\nDask后端的Map操作实现了智能去重功能，通过`SemanticDeduplicator`预先识别语义相似的记录。当存在重复记录群组时，系统仅对每个群组的**canonical（规范）记录**调用LLM，然后将结果复制到群组内的其他重复记录。\n\n```python\ndup_to_canon = {\n    dup: c[\"canonical_id\"]\n    for c in clusters\n    for dup in c[\"duplicate_ids\"]\n}\n\ncanonical_idx = input_series.index.difference(dup_to_canon.keys())\ncanonical_input = input_series.loc[canonical_idx]\n\nllm_out = llm(\n    canonical_input,\n    prefix,\n    prompt,\n    suffix,\n    optimized=True\n)\n\ndf.loc[canonical_idx, llm_output_column] = llm_out\n\nfor dup, canon in dup_to_canon.items():\n    df.loc[dup, llm_output_column] = df.loc[canon, llm_output_column]\n```\n\n资料来源：[datatune/core/dask/map_dask.py:25-46](https://github.com/vitalops/datatune/blob/main/datatune/core/dask/map_dask.py)\n\n#### LLM调用与输出解析\n\nMap操作使用特定的输出格式约束，确保LLM返回可解析的Python字典：\n\n```python\nsuffix = (\n    f\"{os.linesep}{os.linesep}\"\n    \"Your response MUST be the entire input record as a valid Python dictionary in the format\"\n    \"'index=<row_index>|{key1: value1, key2: value2, ...}'  with added keys of expected new fields if any.\"\n     \n    \"ALWAYS START YOUR RESPONSE WITH 'index=<row_index>|' WHERE <row_index> IS THE INDEX OF THE ROW.\" \\\n    \"IF A VALUE FOR A COLUMN DOES NOT EXIST SET IT TO null\" \\\n    \"'index=<row_index>|{key1: None, key2: value2, ...}'\"\n)\n```\n\n输出解析通过`parse_llm_output`函数处理，将LLM返回的字符串转换为Python字典对象。\n\n资料来源：[datatune/core/dask/map_dask.py:8-17](https://github.com/vitalops/datatune/blob/main/datatune/core/dask/map_dask.py)\n\n### Ibis后端 map_ibis.py\n\n#### 行号索引机制\n\nIbis后端为每个输入记录添加行号索引，以便后续结果关联：\n\n```python\nindexed_table = table.mutate(_ROW_ID_=ibis.row_number().cast(\"int64\"))\n\nlocal_data = indexed_table.select(\"_ROW_ID_\", input_col).execute()\n\ninput_list = local_data[input_col].tolist()\n\nraw_results = llm(input_list, prefix, mapping_prompt, suffix, optimized=True)\n```\n\n资料来源：[datatune/core/ibis/map_ibis.py:35-45](https://github.com/vitalops/datatune/blob/main/datatune/core/ibis/map_ibis.py)\n\n#### 结果转换与关联\n\n解析后的结果被转换为内存表并与原表JOIN：\n\n```python\nprocessed_results = []\nfor res in raw_results:\n    try:\n        py_dict = ast.literal_eval(str(res).strip())\n        processed_results.append(json.dumps(py_dict))\n    except Exception:\n        processed_results.append(\"{}\")\n\nmapping_df = pd.DataFrame({\n    \"_ROW_ID_\": local_data[\"_ROW_ID_\"].values, \n    output_col: processed_results\n})\nmapping_table = ibis.memtable(mapping_df)\n```\n\n资料来源：[datatune/core/ibis/map_ibis.py:47-60](https://github.com/vitalops/datatune/blob/main/datatune/core/ibis/map_ibis.py)\n\n## Agent集成\n\n### Plan定义格式\n\n在Agent的Plan中，Map操作作为primitive步骤声明：\n\n```json\n{\n    \"type\": \"primitive\",\n    \"operation\": \"map\",\n    \"params\": {\n        \"subprompt\": \"Extract category and sub-category from industry\",\n        \"input_fields\": [\"Industry\"],\n        \"output_fields\": [\"Category\", \"Sub-Category\"]\n    }\n}\n```\n\n资料来源：[datatune/agent/agent.py:78-85](https://github.com/vitalops/datatune/blob/main/datatune/agent/agent.py)\n\n### 步骤类型决策规则\n\nAgent根据以下规则选择Map操作：\n\n| 条件 | 选择操作类型 |\n|------|-------------|\n| 需要自然语言理解、语义提取、分类 | primitive (Map/Filter) |\n| 涉及多列语义推理 | primitive (Map) |\n| 简单的数值/列操作 | dask |\n| 复杂多步骤转换 | 优先使用dask，后续步骤依赖新列时可使用primitive |\n\n## 使用示例\n\n### 基础用法\n\n```python\nimport datatune as dt\nfrom datatune.llm.llm import OpenAI\nimport dask.dataframe as dd\n\nllm = OpenAI(model_name=\"gpt-3.5-turbo\")\ndf = dd.read_csv(\"products.csv\")\n\n# 提取类别信息\nmapped = dt.map(\n    prompt=\"Extract categories from the description and name of product.\",\n    output_fields=[\"Category\", \"Subcategory\"],\n    input_fields=[\"Description\", \"Name\"]\n)(llm, df)\n\n# 完成转换并保存\nresult = dt.finalize(mapped)\nresult.compute().to_csv(\"mapped_products.csv\")\n```\n\n### 与Agent结合使用\n\n```python\nimport datatune as dt\nfrom datatune.llm.llm import OpenAI\n\nllm = OpenAI(model_name=\"gpt-3.5-turbo\")\nagent = dt.Agent(llm)\n\ndf = agent.do(\"Add ProfitMargin column and keep only African organizations\", df)\nresult = dt.finalize(df)\n```\n\n## API参数说明\n\n### dt.map 函数参数\n\n| 参数 | 类型 | 必填 | 说明 |\n|------|------|------|------|\n| prompt | str | 是 | 自然语言描述的转换规则 |\n| input_fields | List[str] | 是 | 输入列名列表 |\n| output_fields | List[str] | 否 | 新生成的输出列名列表 |\n| optimized | bool | 否 | 是否启用去重优化，默认为True |\n\n### LLM类配置\n\ndatatune支持多种LLM后端，各后端的初始化参数如下：\n\n| LLM类 | 主要参数 | 默认值 |\n|-------|---------|--------|\n| OpenAI | model_name, api_key | gpt-3.5-turbo |\n| Ollama | model_name, api_base | gemma3:4b, http://localhost:11434 |\n| Azure | model_name, api_key, api_base, api_version | - |\n| VLLM | model_name, api_base | - , http://localhost:8000/v1 |\n\n资料来源：[datatune/llm/llm.py:48-95](https://github.com/vitalops/datatune/blob/main/datatune/llm/llm.py)\n\n## 速率限制\n\ndatatune内置了常用模型的速率限制配置，支持tpm（每分钟Token数）和rpm（每分钟请求数）两个维度的控制。\n\n| 模型 | TPM | RPM |\n|------|-----|-----|\n| gpt-3.5-turbo | 200,000 | 500 |\n| gpt-4 | 10,000 | 500 |\n| gpt-4-turbo | 30,000 | 500 |\n| gpt-4.1-mini | 200,000 | 500 |\n\n资料来源：[datatune/llm/model_rate_limits.py:1-30](https://github.com/vitalops/datatune/blob/main/datatune/llm/model_rate_limits.py)\n\n## 技术特性\n\n### 语义去重优化\n\n当数据中存在语义重复的记录时，Map操作通过`SemanticDeduplicator`识别并合并：\n\n```mermaid\ngraph LR\n    A[原始数据] --> B[语义去重]\n    B --> C[Canonical记录]\n    B --> D[重复记录映射]\n    C --> E[单次LLM调用]\n    D --> F[结果复制]\n    E --> G[最终结果]\n    F --> G\n```\n\n此机制可节省高达50%-90%的API调用成本，同时保持结果准确性。\n\n### 分布式处理\n\nMap操作基于Dask或Ibis实现，支持大规模数据集的分布式处理：\n\n- **Dask后端**：利用Dask DataFrame的分区机制，每个分区独立处理\n- **Ibis后端**：支持DuckDB、PostgreSQL、BigQuery等多种数据库后端\n\n## 最佳实践\n\n1. **批量输入优化**：尽量将相关记录放在一起处理，提高去重效果\n2. **明确输出字段**：在`output_fields`中清晰指定新列名，便于后续操作\n3. **选择合适模型**：简单提取任务可使用gpt-3.5-turbo，复杂推理建议使用gpt-4\n4. **结果验证**：首次使用新prompt时建议抽样验证输出质量\n\n## 相关模块\n\n- **Filter操作** (`datatune/core/filter.py`)：基于LLM的过滤功能\n- **Agent** (`datatune/agent/agent.py`)：自动规划并执行Map/Filter/dask操作\n- **SemanticDeduplicator** (`datatune/core/deduplication.py`)：语义去重功能\n- **finalize** (`datatune/core/dask/op.py`)：完成Lazy DataFrame计算\n\n---\n\n<a id='filter-operation'></a>\n\n## Filter操作\n\n### 相关页面\n\n相关主题：[Map操作](#map-operation), [Reduce操作](#reduce-operation)\n\n<details>\n<summary>相关源码文件</summary>\n\n以下源码文件用于生成本页说明：\n\n- [datatune/core/filter.py](https://github.com/vitalops/datatune/blob/main/datatune/core/filter.py)\n- [datatune/core/dask/filter_dask.py](https://github.com/vitalops/datatune/blob/main/datatune/core/dask/filter_dask.py)\n- [datatune/core/ibis/filter_ibis.py](https://github.com/vitalops/datatune/blob/main/datatune/core/ibis/filter_ibis.py)\n</details>\n\n# Filter操作\n\n## 概述\n\nFilter操作是datatune库中用于基于自然语言描述对数据进行行级过滤的核心功能。该操作允许用户使用LLM（大型语言模型）根据语义理解来判断哪些数据行应该被保留或移除，无需编写复杂的条件表达式。\n\nFilter操作的本质是将用户的自然语言过滤条件翻译为布尔判断，通过LLM的语义理解能力实现智能化的数据筛选。系统会在每条数据记录中添加一个`__filter__`字段，值为`True`表示保留该记录，`False`表示移除该记录。\n\n资料来源：[datatune/core/filter.py:1-30]()\n\n## 架构设计\n\n### 模块结构\n\nFilter操作采用后端分发的设计模式，根据输入数据的类型自动选择相应的处理后端。顶层模块`filter.py`负责类型检测和后端路由，具体实现分布在Dask和Ibis两个后端模块中。\n\n```mermaid\ngraph TD\n    A[用户调用 dt.filter] --> B{数据类型检测}\n    B -->|Dask DataFrame| C[_filter_dask]\n    B -->|Ibis Table| D[_filter_ibis]\n    B -->|不支持的类型| E[TypeError]\n    \n    C --> F[LLM批量推理]\n    D --> F\n    \n    F --> G[解析LLM输出]\n    G --> H[生成过滤序列]\n    H --> I[执行过滤操作]\n```\n\n资料来源：[datatune/core/filter.py:16-29]()\n\n### 后端支持\n\n| 后端类型 | 处理模块 | 支持的数据源 |\n|---------|---------|-------------|\n| Dask | `datatune/core/dask/filter_dask.py` | Dask DataFrame |\n| Ibis | `datatune/core/ibis/filter_ibis.py` | Ibis Table（DuckDB、PostgreSQL、BigQuery等） |\n\n资料来源：[datatune/core/filter.py:18-27]()\n\n## API接口\n\n### 函数签名\n\n```python\ndef filter(*, prompt, input_fields=None, clusters=None):\n    def apply(llm, data):\n        # 后端分发逻辑\n        pass\n    return apply\n```\n\n### 参数说明\n\n| 参数 | 类型 | 必填 | 说明 |\n|-----|------|------|------|\n| `prompt` | str | 是 | 自然语言描述的过滤条件 |\n| `input_fields` | List[str] | 否 | 要参与过滤判断的输入列（当前版本中未实际使用） |\n| `clusters` | List[Dict] | 否 | 用于去重场景的聚类信息，避免重复调用LLM |\n\n资料来源：[datatune/core/filter.py:12-28]()\n\n### 使用示例\n\n```python\nimport datatune as dt\nfrom datatune.llm.llm import OpenAI\nimport dask.dataframe as dd\n\nllm = OpenAI(model_name=\"gpt-3.5-turbo\")\ndf = dd.read_csv(\"products.csv\")\n\n# 使用自然语言过滤数据\nfiltered = dt.filter(\n    prompt=\"Keep only electronics products\",\n    input_fields=[\"Name\"]\n)(llm, df)\n\nresult = dt.finalize(filtered)\n```\n\n资料来源：[README.md:1-45]()\n\n## 工作流程\n\n### 整体流程\n\nFilter操作的处理流程分为以下几个阶段：\n\n```mermaid\ngraph LR\n    A[序列化输入数据] --> B[构建LLM提示词]\n    B --> C[批量调用LLM]\n    C --> D[解析LLM输出]\n    D --> E[提取__filter__字段]\n    E --> F[生成布尔序列]\n    F --> G[执行数据过滤]\n```\n\n### 提示词构建\n\n系统自动构建包含前缀、用户提示和后缀的完整提示词。\n\n**前缀部分**包含输入数据格式规范：\n```\nEach answer must be formatted exactly as 'index=<index>|{answer}<endofrow>'.\n{answer} must be any requested python literal (e.g. list, dict, string, integer)\neg: for a dict answer: index=1|{'key1': 'value1', 'key2': 2}<endofrow>\nEnsure that each response is a valid python literal.\nEnsure that strings are enclosed in double quotes.\nSTRINGS MUST BE ENCLOSED IN DOUBLE QUOTES. DO NOT OUTPUT BARE TEXT\n```\n\n**用户提示部分**包含过滤条件：\n```\nFILTERING CRITERIA:\n{prompt}\n```\n\n**后缀部分**指定输出格式要求：\n```\nDECISION: Your response MUST be the entire input record as Python dictionary in the format: index=<row_index>|{key1: value1, key2: value2, ...}<endofrow> with added key called '__filter__' with value either True to KEEP the record or False to REMOVE it.\nNo explanations or additional text.\nALWAYS STICK TO THE FORMAT index=<row_index>|{key1: value1, key2: value2, ...}<endofrow> with added key called '__filter__' with value either True to KEEP the record or False to REMOVE it.\nIF A VALUE FOR A COLUMN DOES NOT EXIST SET IT TO None\n```\n\n资料来源：[datatune/core/dask/filter_dask.py:1-30]()\n\n### LLM响应格式\n\nLLM返回的数据必须包含原始记录信息，并添加`__filter__`布尔字段：\n\n```python\n# 示例输入\n{\"Name\": \"Laptop\", \"Price\": 999, \"Category\": \"Electronics\"}\n\n# LLM期望输出格式\n'index=0|{\"Name\": \"Laptop\", \"Price\": 999, \"Category\": \"Electronics\", \"__filter__\": true}<endofrow>'\n```\n\n## Dask后端实现\n\n### 去重机制\n\nDask后端支持基于聚类的去重优化。当提供`clusters`参数时，系统会建立一个从重复ID到规范ID的映射：\n\n```python\ndup_to_canon = {\n    dup: c[\"canonical_id\"]\n    for c in clusters\n    for dup in c[\"duplicate_ids\"]\n}\n\ncanonical_idx = input_series.index.difference(dup_to_canon.keys())\ncanonical_input = input_series.loc[canonical_idx]\n```\n\n对于规范数据行调用LLM进行判断，然后将结果复制到所有重复行：\n\n```python\nfor dup, canon in dup_to_canon.items():\n    df.loc[dup, llm_output_column] = df.loc[canon, llm_output_column]\n```\n\n资料来源：[datatune/core/dask/filter_dask.py:25-50]()\n\n### 过滤序列生成\n\n使用`parse_filter_output`函数解析LLM输出：\n\n```python\ndef parse_filter_output(\n    output: Union[str, Exception], err: bool = True\n) -> Optional[bool]:\n    # 解析LLM返回的字典，提取__filter__字段值\n```\n\n资料来源：[datatune/core/dask/filter_dask.py:60-75]()\n\n## Ibis后端实现\n\n### 行号索引\n\nIbis后端通过`row_number()`函数为表添加行号索引：\n\n```python\nindexed_table = table.mutate(_ROW_ID_=ibis.row_number().cast(\"int64\"))\nlocal_data = indexed_table.select(\"_ROW_ID_\", input_col).execute()\n```\n\n资料来源：[datatune/core/ibis/filter_ibis.py:20-25]()\n\n### 数据处理\n\nIbis实现同样通过AST解析提取过滤决策，并将结果存储在内存表中进行关联：\n\n```python\nmapping_df = pd.DataFrame({\n    \"_ROW_ID_\": local_data[\"_ROW_ID_\"].values, \n    output_col: processed_results\n})\nmapping_table = ibis.memtable(mapping_df)\n```\n\n资料来源：[datatune/core/ibis/filter_ibis.py:35-45]()\n\n## 与Agent的集成\n\nFilter操作可以与datatune的Agent功能结合使用，让AI自动判断何时需要使用Filter操作：\n\n```python\nagent = dt.Agent(llm)\ndf = agent.do(\"Keep only African organizations\", df)\n```\n\n在Agent的规划模板中，Filter操作被定义为primitive类型：\n\n```python\n\"filter\": textwrap.dedent(\n    \"\"\"\\\n    filtered = dt.filter(\n        prompt=\"{subprompt}\",\n        input_fields={input_fields}\n    )(llm, df)\n    df = filtered\n    \"\"\"\n)\n```\n\n资料来源：[datatune/agent/agent.py:100-110]()\n\n## 错误处理\n\n| 错误类型 | 处理方式 |\n|---------|---------|\n| 不支持的数据类型 | 抛出`TypeError`，提示当前支持Dask和Ibis |\n| LLM输出解析失败 | 捕获异常，返回空字典或默认值 |\n| API调用异常 | 通过异常处理机制传递给上层 |\n\n资料来源：[datatune/core/filter.py:27-28]()\n\n## 最佳实践\n\n### 输入数据准备\n\n确保输入数据包含足够的上下文信息供LLM进行判断。对于涉及多列条件的过滤，应在prompt中明确说明需要参考的列。\n\n### 批量处理\n\nFilter操作支持批量处理，通过设置合理的批次大小来平衡处理速度和API调用频率。\n\n### 去重优化\n\n在处理可能包含重复记录的数据时，使用`clusters`参数可以显著减少LLM API调用次数，提升处理效率。\n\n---\n\n<a id='reduce-operation'></a>\n\n## Reduce操作\n\n### 相关页面\n\n相关主题：[Map操作](#map-operation), [Filter操作](#filter-operation)\n\n<details>\n<summary>相关源码文件</summary>\n\n以下源码文件用于生成本页说明：\n\n- [datatune/core/reduce.py](https://github.com/vitalops/datatune/blob/main/datatune/core/reduce.py)\n- [datatune/__init__.py](https://github.com/vitalops/datatune/blob/main/datatune/__init__.py)\n- [datatune/core/dask/map_dask.py](https://github.com/vitalops/datatune/blob/main/datatune/core/dask/map_dask.py)\n- [datatune/core/dask/filter_dask.py](https://github.com/vitalops/datatune/blob/main/datatune/core/dask/filter_dask.py)\n- [datatune/core/deduplication.py](https://github.com/vitalops/datatune/blob/main/datatune/core/deduplication.py)\n</details>\n\n# Reduce操作\n\n## 概述\n\nReduce操作是datatune库中用于对数据执行聚合、归约和转换操作的核心模块之一。该模块采用插件化架构设计，允许通过注册机制扩展不同的归约操作类型。`reduce`函数作为统一入口，接收数据帧和操作类型参数，动态调用对应的归约处理器完成数据处理任务。\n\n在datatune的生态系统定位中，Reduce操作与Map、Filter操作共同构成了基于大语言模型的数据处理管道。三者的职责分工如下：\n\n| 操作类型 | 职责 | 输入 | 输出 |\n|---------|------|------|------|\n| Map | 字段映射与转换 | 单行数据 | 添加新字段的行数据 |\n| Filter | 数据过滤 | 单行数据 | 布尔决策（保留/移除） |\n| Reduce | 聚合与归约 | 多行数据 | 聚合结果 |\n\n## 核心架构\n\n### 插件注册机制\n\nReduce模块采用工厂模式和装饰器模式实现插件化架构。核心组件包括全局动作注册表、动作注册装饰器和动作获取函数三个部分。\n\n```mermaid\ngraph TD\n    A[reduce函数] --> B[get_action获取动作类]\n    B --> C{查找_ACTIONS注册表}\n    C -->|找到| D[实例化动作类]\n    C -->|未找到| E[抛出ValueError异常]\n    D --> F[调用reducer执行归约]\n    F --> G[返回处理结果]\n    \n    H[register_action装饰器] --> I[将动作类注册到_ACTIONS]\n    J[自定义动作类] --> H\n    K[内置动作类] --> H\n```\n\n### 全局动作注册表\n\n```python\n_ACTIONS = {}\n```\n\n`_ACTIONS`是一个模块级字典，用作动作类的注册表。键为动作名称字符串，值为对应的动作类。资料来源：[datatune/core/reduce.py:1]()\n\n### 注册装饰器\n\n```python\ndef register_action(name):\n    def decorator(cls):\n        _ACTIONS[name] = cls\n        return cls\n    return decorator\n```\n\n`register_action`装饰器接收动作名称作为参数，返回一个接受类作为参数的装饰器函数。装饰器执行时将类注册到`_ACTIONS`字典中，并返回原类以保持装饰器链的正常工作。资料来源：[datatune/core/reduce.py:6-12]()\n\n### 动作获取函数\n\n```python\ndef get_action(name):\n    try:\n        return _ACTIONS[name]\n    except KeyError:\n        raise ValueError(f\"Unknown action: {name}\")\n```\n\n`get_action`函数从注册表中查找指定名称的动作类。若未找到对应动作，抛出`ValueError`异常并提示未知动作名称。资料来源：[datatune/core/reduce.py:14-20]()\n\n## API参考\n\n### reduce函数\n\n```python\ndef reduce(df, *, action: str, **kwargs):\n    cls = get_action(action)\n    reducer = cls(**kwargs)\n    return reducer(df)\n```\n\n**函数签名参数说明：**\n\n| 参数 | 类型 | 必填 | 说明 |\n|------|------|------|------|\n| `df` | `dask.dataframe.DataFrame` | 是 | 输入数据帧 |\n| `action` | `str` | 是 | 动作类型名称，用于从注册表获取对应动作类 |\n| `**kwargs` | 任意 | 否 | 传递给动作类构造器的关键字参数 |\n\n**返回值：** 返回动作类实例调用后的结果，具体类型取决于具体动作的实现。\n\n资料来源：[datatune/core/reduce.py:23-27]()\n\n## 使用方式\n\n### 基本调用模式\n\n```python\nimport datatune as dt\nimport dask.dataframe as dd\n\n# 读取数据\ndf = dd.read_csv(\"data.csv\")\n\n# 调用reduce操作\nresult = dt.reduce(df, action=\"your_action_name\", param1=value1, param2=value2)\n```\n\n### 在管道中使用\n\nReduce操作可以与其他datatune操作链式调用，构建完整的数据处理管道：\n\n```python\nimport datatune as dt\nfrom datatune.llm.llm import OpenAI\n\nllm = OpenAI(model_name=\"gpt-3.5-turbo\")\n\n# 构建处理管道\ndf = dd.read_csv(\"raw_data.csv\")\nmapped = dt.map(prompt=\"提取产品类别\", input_fields=[\"description\"], output_fields=[\"category\"])(llm, df)\nfiltered = dt.filter(prompt=\"只保留电子产品\", input_fields=[\"category\"])(llm, mapped)\nresult = dt.reduce(df, action=\"deduplicate\", embedding_model=\"text-embedding-3-small\")\nfinal_result = dt.finalize(result)\n```\n\n## 内置归约动作\n\n### SemanticDeduplicator\n\n语义去重动作，用于识别和合并语义相似的记录。该动作使用嵌入向量计算记录间的语义相似度，适用于需要处理近似重复数据的场景。\n\n**嵌入到磁盘功能：**\n\n```python\ndef embed_column_to_disk(self, df, column, output_dir):\n    os.makedirs(output_dir, exist_ok=True)\n\n    tasks = []\n\n    for pid in range(df.npartitions):\n        part = df[column].get_partition(pid)\n        task = dask.delayed(self._embed_and_write_partition)(\n            part,\n            pid,\n            output_dir,\n        )\n        tasks.append(task)\n\n    dask.compute(*tasks)\n```\n\n资料来源：[datatune/core/deduplication.py:1-100]()\n\n**分区嵌入处理逻辑：**\n\n```python\ndef _embed_and_write_partition(self, part, partition_id, output_dir):\n    pdf = part\n\n    if pdf.empty:\n        print(\"empty partition\")\n        return 0\n\n    emb_path = f\"{output_dir}/embeddings_part_{partition_id}.npy\"\n    idx_path = f\"{output_dir}/index_part_{partition_id}.npy\"\n\n    if os.path.exists(emb_path) and os.path.exists(idx_path):\n        return 0\n\n    row_index = pdf.index.to_numpy()\n\n    def safe_parse(row):\n        try:\n            return ast.literal_eval(row)\n        except Exception:\n            return {}\n\n    dicts = [safe_parse(row) for row in pdf]\n\n    texts = [\n        \", \".join(f\"{k}: {v}\" for k, v in d.items())\n        for d in dicts\n    ]\n\n    embeddings = []\n    for i in range(0, len(texts), 256):\n        batch = texts[i:i+256]\n        resp = embedding(model=self.embedding_model, input=batch)\n        embeddings.extend([item[\"embedding\"] for item in resp[\"data\"]])\n\n    X = np.asarray(embeddings, dtype=\"float32\")\n    faiss.normalize_L2(X)\n\n    np.save(f\"{output_dir}/embeddings_part_{partition_id}.npy\", X)\n    np.save(f\"{output_dir}/index_part_{partition_id}.npy\", row_index)\n```\n\n资料来源：[datatune/core/deduplication.py:70-130]()\n\n**FAISS索引构建：**\n\n```python\ndef build_faiss_index(self, embedding_dir, dim, hnsw_m, ef_search):\n    index = faiss.IndexHNSWFlat(\n        dim,\n        hnsw_m,\n        faiss.METRIC_INNER_PRODUCT,\n    )\n    index.hnsw.efConstruction = 200\n```\n\n资料来源：[datatune/core/deduplication.py:140-160]()\n\n### 构造器参数\n\n| 参数 | 类型 | 默认值 | 说明 |\n|------|------|--------|------|\n| `llm` | `LLM` | 无 | 用于执行去重的语言模型实例 |\n| `embedding_model` | `str` | `\"text-embedding-3-small\"` | 嵌入模型名称 |\n| `sim_threshold` | `float` | `0.90` | 相似度阈值，超过此值的记录被视为重复 |\n| `top_k` | `int` | `50` | 每个记录查找的最大相似记录数 |\n| `hnsw_m` | `int` | `32` | HNSW索引的空间参数M |\n| `ef_search` | `int` | `64` | HNSW搜索时的搜索参数ef |\n| `return_df` | `bool` | `False` | 是否返回DataFrame格式结果 |\n\n资料来源：[datatune/core/deduplication.py:160-180]()\n\n## 自定义归约动作\n\n用户可以通过`@register_action`装饰器注册自定义归约动作，扩展reduce模块的功能。\n\n### 创建自定义动作示例\n\n```python\nfrom datatune.core.reduce import register_action\n\n@register_action(\"group_stats\")\nclass GroupStatsReducer:\n    def __init__(self, group_by_column, stat_columns):\n        self.group_by_column = group_by_column\n        self.stat_columns = stat_columns\n    \n    def __call__(self, df):\n        return df.groupby(self.group_by_column)[self.stat_columns].agg(['mean', 'sum', 'count'])\n```\n\n### 调用自定义动作\n\n```python\nimport datatune as dt\n\nresult = dt.reduce(df, action=\"group_stats\", group_by_column=\"category\", stat_columns=[\"price\", \"quantity\"])\n```\n\n## 执行流程\n\n```mermaid\nsequenceDiagram\n    participant User as 用户\n    participant Reduce as reduce函数\n    participant Registry as 动作注册表\n    participant Action as 动作类\n    participant Result as 处理结果\n\n    User->>Reduce: reduce(df, action=\"xxx\", **kwargs)\n    Reduce->>Registry: get_action(\"xxx\")\n    Registry-->>Reduce: 动作类引用\n    Reduce->>Action: 实例化 cls(**kwargs)\n    Reduce->>Action: reducer(df)\n    Action->>Result: 执行归约逻辑\n    Result-->>User: 返回处理结果\n```\n\n## 与Map和Filter的协作\n\n在datatune的整体架构中，Reduce操作通常与其他两种操作配合使用。Map操作用于生成新的列字段，Filter操作用于数据筛选，Reduce操作用于聚合和去重。\n\n```mermaid\ngraph LR\n    A[原始数据] --> B[Map操作]\n    B --> C[新增列数据]\n    C --> D[Filter操作]\n    D --> E[筛选后数据]\n    E --> F[Reduce操作]\n    F --> G[聚合/去重结果]\n    G --> H[finalize]\n    H --> I[计算结果]\n```\n\n### 工作流程示例\n\n```python\nimport datatune as dt\nfrom datatune.llm.llm import OpenAI\nimport dask.dataframe as dd\n\nllm = OpenAI(model_name=\"gpt-3.5-turbo\")\ndf = dd.read_csv(\"products.csv\")\n\n# Step 1: 使用Map提取类别信息\nmapped = dt.map(\n    prompt=\"从产品描述中提取主要类别\",\n    output_fields=[\"Category\"],\n    input_fields=[\"Description\", \"Name\"]\n)(llm, df)\n\n# Step 2: 使用Filter筛选特定类别\nfiltered = dt.filter(\n    prompt=\"只保留电子产品\",\n    input_fields=[\"Category\"]\n)(llm, mapped)\n\n# Step 3: 使用Reduce进行语义去重\ndeduplicated = dt.reduce(\n    df=dt.finalize(filtered),\n    action=\"deduplicate\",\n    embedding_model=\"text-embedding-3-small\"\n)\n\n# Step 4: 最终计算输出\nresult = deduplicated.compute()\n```\n\n资料来源：[datatune/__init__.py:1-10]()\n\n## 注意事项\n\n1. **动作注册时机**：自定义动作需要在调用`reduce`函数之前完成注册，确保动作类已在`_ACTIONS`注册表中可用。\n\n2. **参数传递**：`reduce`函数使用关键字参数`**kwargs`将配置传递给动作类构造器，调用者需确保参数名与动作类构造器参数匹配。\n\n3. **异常处理**：当指定的动作名称不存在时，`get_action`函数会抛出`ValueError`异常。应用程序应做好异常捕获处理。\n\n4. **Dask集成**：Reduce操作基于Dask DataFrame实现，支持大规模数据的分布式处理。输入数据应为`dask.dataframe.DataFrame`类型。资料来源：[datatune/core/reduce.py:1]()\n\n5. **分区处理**：在处理大规模数据时，系统会按分区逐个处理，用户应合理配置Dask集群或本地并行参数以优化性能。\n\n---\n\n<a id='agent-system'></a>\n\n## Agent系统\n\n### 相关页面\n\n相关主题：[LLM集成](#llm-integration), [Map操作](#map-operation), [Filter操作](#filter-operation)\n\n<details>\n<summary>相关源码文件</summary>\n\n以下源码文件用于生成本页说明：\n\n- [datatune/agent/agent.py](https://github.com/vitalops/datatune/blob/main/datatune/agent/agent.py)\n- [datatune/agent/__init__.py](https://github.com/vitalops/datatune/blob/main/datatune/agent/__init__.py)\n- [datatune/llm/llm.py](https://github.com/vitalops/datatune/blob/main/datatune/llm/llm.py)\n- [datatune/__init__.py](https://github.com/vitalops/datatune/blob/main/datatune/__init__.py)\n- [datatune/core/dask/filter_dask.py](https://github.com/vitalops/datatune/blob/main/datatune/core/dask/filter_dask.py)\n- [datatune/core/dask/map_dask.py](https://github.com/vitalops/datatune/blob/main/datatune/core/dask/map_dask.py)\n</details>\n\n# Agent系统\n\n## 概述\n\nAgent系统是Datatune的核心智能组件，它允许用户使用自然语言描述数据处理目标，系统自动解析用户意图，生成并执行数据转换计划。该系统结合了大型语言模型（LLM）的推理能力与Dask分布式计算框架，能够处理复杂的多步骤数据转换任务，无需用户手动编写代码。\n\nAgent的核心设计理念是：**用户描述期望的结果，系统自动决定使用何种操作以及如何组合这些操作**。这使得非技术用户也能完成复杂的数据处理任务，同时保持足够的灵活性供高级用户使用。\n\n资料来源：[datatune/agent/__init__.py:1-30]()\n\n## 架构设计\n\n### 系统组件\n\nAgent系统由以下核心组件构成：\n\n| 组件 | 文件位置 | 职责 |\n|------|---------|------|\n| Agent | `datatune/agent/agent.py` | 主要执行引擎，负责计划生成与执行 |\n| Agent基类 | `datatune/agent/__init__.py` | 定义系统提示词和抽象接口 |\n| LLM | `datatune/llm/llm.py` | 提供多后端LLM调用能力 |\n| Map原语 | `datatune/core/dask/map_dask.py` | 基于LLM的列映射转换 |\n| Filter原语 | `datatune/core/dask/filter_dask.py` | 基于LLM的行过滤 |\n\n### 数据流架构\n\n```mermaid\ngraph TD\n    A[用户自然语言目标] --> B[Agent.do方法]\n    B --> C[LLM生成计划JSON]\n    C --> D[计划验证]\n    D --> E{计划类型}\n    E -->|dask| F[Dask操作模板]\n    E -->|primitive| G[Map/Filter原语]\n    F --> H[代码执行引擎]\n    G --> H\n    H --> I[错误处理]\n    I -->|成功| J[最终结果]\n    I -->|失败| K[错误恢复]\n    K --> C\n```\n\nAgent接收用户的自然语言指令后，首先调用LLM生成结构化的执行计划。计划由一系列步骤组成，每个步骤可以是Dask原生操作或LLM驱动的原语操作。\n\n资料来源：[datatune/agent/agent.py:1-50]()\n\n## 核心类设计\n\n### Agent类\n\n`Agent`是系统的主要执行类，封装了所有数据处理逻辑。\n\n```python\nclass Agent:\n    def __init__(self, llm: LLM, verbose: bool = False):\n        self.llm = llm\n        self.history: List[Dict[str, Any]] = []\n        self.verbose = verbose\n```\n\n**初始化参数：**\n\n| 参数 | 类型 | 必填 | 说明 |\n|------|------|------|------|\n| `llm` | `LLM` | 是 | LLM实例，用于生成计划和执行原语 |\n| `verbose` | `bool` | 否 | 设为True时启用DEBUG级别日志，默认False |\n\n**主要方法：**\n\n| 方法 | 返回值 | 说明 |\n|------|--------|------|\n| `do(goal: str, df: dd.DataFrame)` | `None \\| str` | 执行自然语言目标 |\n| `_generate_plan(goal)` | `List[Dict]` | 生成执行计划 |\n| `_execute_plan(plan)` | `Tuple[None, int] \\| Tuple[str, int]` | 执行完整计划 |\n| `_execute_step(step)` | `None \\| str` | 执行单个步骤 |\n| `log_primitive(df, message)` | `dd.DataFrame` | 记录原语执行状态 |\n\n资料来源：[datatune/agent/agent.py:50-80]()\n\n### Agent基类\n\n```python\nclass Agent(ABC):\n    system_prompt: str = \"\"\"You are Datatune Agent, a powerful assistant designed to help users with data processing tasks.\n    You are capable of generating python code to perform various operations on data. Apart from python builtins, you have the following libraries avaiable in your run time:\n    - pandas\n    - numpy\n    - dask\n\n    In addition to these, you also have access to the datatune libarary, which provides functionality for processing data using LLMs.\n    ...\"\"\"\n```\n\n基类定义了Agent的系统提示词，明确了可用的运行时环境：Python内置库、pandas、numpy、dask以及datatune库。\n\n资料来源：[datatune/agent/__init__.py:1-30]()\n\n## 执行计划模型\n\n### 计划结构\n\n执行计划是一个JSON数组，每个元素代表一个独立的执行步骤：\n\n```json\n[\n  {\n    \"type\": \"dask\" | \"primitive\",\n    \"operation\": \"操作名称\",\n    \"params\": { ... },\n    \"subprompt\": \"LLM提示词\",\n    \"input_fields\": [\"列名\"],\n    \"output_fields\": [\"新列名\"]\n  }\n]\n```\n\n### 步骤类型\n\nAgent支持两种类型的操作步骤：\n\n| 类型 | 说明 | 示例操作 |\n|------|------|----------|\n| `dask` | 直接的Dask DataFrame操作 | `add_column`, `group_by`, `rename_columns` |\n| `primitive` | LLM驱动的原语操作 | `Map`, `Filter` |\n\n### Dask操作模板\n\n系统预定义了丰富的Dask操作模板：\n\n| 操作名称 | 功能描述 | 关键参数 |\n|----------|----------|----------|\n| `add_column` | 添加新列（基于表达式） | `column`, `expr` |\n| `apply_function` | 对列应用函数 | `column`, `func` |\n| `rename_columns` | 重命名列 | `mapping` |\n| `astype_column` | 更改列数据类型 | `column`, `dtype` |\n\n资料来源：[datatune/agent/agent.py:80-150]()\n\n## 原语系统\n\n### Map原语\n\n`Map`原语使用LLM根据自然语言提示词从现有列生成新列。它能够处理语义提取、分类、解释等复杂转换任务。\n\n**工作流程：**\n\n1. 序列化输入列数据为字典格式\n2. 将数据分批发送给LLM\n3. LLM返回包含新字段的字典\n4. 将结果合并回DataFrame\n\n```python\nmap = dt.Map(prompt=\"Extract categories from the description\")\nmapped_df = map(llm, df)\n```\n\n资料来源：[datatune/core/dask/map_dask.py:1-60]()\n\n### Filter原语\n\n`Filter`原语使用LLM根据自然语言条件过滤DataFrame行。\n\n**输出格式规范：**\n\n```\nindex=<row_index>|{...字典内容..., '__filter__': True|False}<endofrow>\n```\n\n- `True`：保留该行\n- `False`：移除该行\n\n资料来源：[datatune/core/dask/filter_dask.py:1-50]()\n\n## LLM后端支持\n\n系统通过统一的`LLM`接口支持多种语言模型提供商：\n\n```mermaid\ngraph LR\n    A[LLM基类] --> B[OpenAI]\n    A --> C[Ollama]\n    A --> D[VLLM]\n    A --> E[Azure]\n```\n\n### 支持的模型\n\n| 提供商 | 模型标识 | 默认模型 |\n|--------|----------|----------|\n| OpenAI | `openai/<model>` | `gpt-3.5-turbo` |\n| Ollama | `ollama_chat/<model>` | `gemma3:4b` |\n| VLLM | `openai/<model>` | - |\n| Azure | `azure/<model>` | - |\n\n### 速率限制\n\n系统内置了主流模型的速率限制配置：\n\n| 模型 | TPM (每分钟Token数) | RPM (每分钟请求数) |\n|------|---------------------|-------------------|\n| `gpt-3.5-turbo` | 200,000 | 500 |\n| `gpt-4` | 10,000 | 500 |\n| `gpt-4-turbo` | 30,000 | 500 |\n| `gpt-4o` | 30,000 | 500 |\n\n资料来源：[datatune/llm/model_rate_limits.py:1-80]()\n\n## 使用示例\n\n### 基础用法\n\n```python\nimport datatune as dt\nfrom datatune.llm.llm import OpenAI\nimport dask.dataframe as dd\n\n# 初始化LLM和Agent\nllm = OpenAI(model_name=\"gpt-3.5-turbo\")\nagent = dt.Agent(llm)\n\n# 加载数据\ndf = dd.read_csv(\"products.csv\")\n\n# 使用自然语言描述任务\ndf = agent.do(\"Add ProfitMargin column and keep only African organizations\", df)\nresult = dt.finalize(df)\n```\n\n### 详细示例\n\n```python\n# 创建Agent实例（启用详细日志）\nagent = dt.Agent(llm, verbose=True)\n\n# 执行复杂的多步骤任务\ngoal = \"\"\"\n1. Create a 'PriceCategory' column: 'high' if price > 100, 'low' otherwise\n2. Keep only rows where PriceCategory is 'high'\n3. Add 'DiscountedPrice' column: Price * 0.9\n\"\"\"\n\ndf = agent.do(goal, df)\nresult = dt.finalize(df)\n```\n\n### 单独使用Map和Filter\n\n```python\n# 仅使用Map原语\nmapped = dt.map(\n    prompt=\"Extract categories from the description and name\",\n    output_fields=[\"Category\", \"Subcategory\"],\n    input_fields=[\"Description\", \"Name\"]\n)(llm, df)\n\n# 仅使用Filter原语\nfiltered = dt.filter(\n    prompt=\"Keep only electronics products\",\n    input_fields=[\"Name\"]\n)(llm, mapped)\n```\n\n## 执行流程详解\n\n### 计划生成\n\n```mermaid\nsequenceDiagram\n    participant User as 用户\n    participant Agent as Agent\n    participant LLM as LLM\n    participant Schema as 数据Schema\n\n    User->>Agent: do(goal, df)\n    Agent->>Schema: 获取DataFrame结构\n    Agent->>LLM: 发送persona_prompt + schema_prompt\n    LLM->>Agent: 返回JSON计划\n    Agent->>Agent: 验证计划格式\n```\n\nAgent生成计划时，会向LLM传递：\n1. **Persona提示词**：定义LLM的角色、能力边界和输出格式\n2. **Schema提示词**：DataFrame的列结构和数据类型\n3. **用户目标**：自然语言描述的期望结果\n\n### 计划执行\n\n执行过程采用流式日志记录：\n\n```python\n# 每个步骤的执行流程\ndf = df.map_partitions(log_primitive, \"🔄 开始步骤 X/Y\", meta=df._meta)\nstep_num += 1\n# 执行具体操作\ndf = df.map_partitions(log_primitive, \"📍 完成步骤 X/Y\", meta=df._meta)\n```\n\n最终执行完成后，调用`dt.finalize()`和`compute()`将Dask DataFrame转换为实际的Pandas DataFrame。\n\n资料来源：[datatune/agent/agent.py:100-140]()\n\n## 错误处理与恢复\n\n### 错误检测机制\n\n系统在每个步骤执行后都会检测异常：\n\n```python\ntry:\n    # 执行步骤\n    ...\nexcept Exception as e:\n    error_msg = f\"{type(e).__name__}: {str(e)}\\n{traceback.format_exc()}\"\n    return error_msg\n```\n\n### 错误恢复流程\n\n当步骤执行失败时，系统会：\n1. 记录错误信息和失败的步骤详情\n2. 生成包含错误上下文的修复提示词\n3. 请求LLM生成修正后的计划\n\n```python\ndef get_error_prompt(self, error_msg: str, failed_step: Dict) -> str:\n    error_prompt = f\"\"\"\n    The previous code execution failed with the following error:\n    Error: {error_msg}\n    Failed step: {failed_step}\n    Provide the json plan with the corrected step.\n    \"\"\"\n```\n\n## 配置与调优\n\n### 初始化配置\n\n```python\n# 完整配置示例\nagent = dt.Agent(\n    llm=llm,\n    verbose=True  # 启用DEBUG日志\n)\n\n# 自定义LLM配置\nllm = OpenAI(\n    model_name=\"gpt-4-turbo\",\n    api_key=\"your-api-key\",\n    rpm=1000,  # 自定义RPM限制\n    tpm=50000  # 自定义TPM限制\n)\n```\n\n### 日志级别控制\n\n```python\nimport logging\n\n# 启用详细日志\nagent = dt.Agent(llm, verbose=True)\nlogger.setLevel(logging.DEBUG)\n\n# 禁用详细日志\nagent = dt.Agent(llm, verbose=False)\nlogger.setLevel(logging.INFO)\n```\n\n## 最佳实践\n\n1. **明确指定列名**：在目标描述中明确提及要操作的列名，有助于Agent生成准确的操作计划\n\n2. **分解复杂任务**：对于非常复杂的任务，可以考虑分步执行以便于调试\n\n3. **选择合适的模型**：简单的数据转换可使用`gpt-3.5-turbo`，复杂的语义任务建议使用`gpt-4-turbo`或`gpt-4o`\n\n4. **监控执行过程**：通过设置`verbose=True`监控Agent的决策过程，便于理解系统行为\n\n5. **处理重复数据**：系统内置了基于语义相似度的去重功能，可用于处理LLM输出中的重复结果\n\n---\n\n<a id='llm-integration'></a>\n\n## LLM集成\n\n### 相关页面\n\n相关主题：[Agent系统](#agent-system)\n\n<details>\n<summary>相关源码文件</summary>\n\n以下源码文件用于生成本页说明：\n\n- [datatune/llm/llm.py](https://github.com/vitalops/datatune/blob/main/datatune/llm/llm.py)\n- [datatune/llm/model_rate_limits.py](https://github.com/vitalops/datatuple/blob/main/datatune/llm/model_rate_limits.py)\n</details>\n\n# LLM集成\n\n## 概述\n\ndatatune 的 LLM 集成模块提供了统一的接口来连接各种大语言模型（Large Language Model）提供者。该模块封装了不同 LLM 服务商的 API 调用逻辑，使上层应用能够以一致的方式使用不同的模型，而无需关心底层实现的差异。\n\n主要功能包括：\n\n- **统一抽象**：通过基类 `LLM` 提供一致的接口设计\n- **多提供商支持**：支持 OpenAI、Ollama、VLLM、Azure、Mistral、Huggingface 等主流 LLM 服务\n- **速率限制管理**：内置对各模型速率限制的支持，防止 API 调用超出限制\n- **Token 计数与批处理**：提供 token 计数和批量请求功能，优化 API 使用效率\n\n## 架构设计\n\n### 类继承结构\n\n```mermaid\ngraph TD\n    LLMBase[LLM 基类] --> Ollama\n    LLMBase --> OpenAI\n    LLMBase --> VLLM\n    LLMBase --> Azure\n    LLMBase --> Gemini\n    LLMBase --> Mistral\n    LLMBase --> Huggingface\n    \n    LLMBase --> 完成方法[_completion]\n    LLMBase --> 批处理方法[_create_batched_prompts]\n    LLMBase --> Token限制[MAX_RPM, MAX_TPM]\n```\n\n### 核心类说明\n\n#### LLM 基类\n\n`LLM` 类是所有 LLM 提供者的基类，定义通用接口和默认行为：\n\n```python\nclass LLM:\n    def __init__(self, model_name: str, **kwargs) -> None:\n        self.model_name = model_name  # 格式: \"provider/model\"\n        self._base_model_name = model_name.split(\"/\", 1)[1]  # 如 \"gpt-3.5-turbo\"\n```\n\n**资料来源**：[datatune/llm/llm.py:27-32]()\n\n#### 速率限制\n\n每个模型实例具有两个关键属性：\n\n| 属性 | 说明 | 来源 |\n|------|------|------|\n| `MAX_RPM` | 每分钟最大请求数 (Requests Per Minute) | model_rate_limits |\n| `MAX_TPM` | 每分钟最大 Token 数 (Tokens Per Minute) | model_rate_limits |\n| `max_tokens` | 单次请求最大 Token 数 | `get_max_tokens()` |\n\n**资料来源**：[datatune/llm/llm.py:62-64]()\n\n## 支持的 LLM 提供者\n\n### 1. OpenAI\n\nOpenAI 模型使用 `openai/` 前缀标识：\n\n```python\nfrom datatune.llm.llm import OpenAI\n\nllm = OpenAI(model_name=\"gpt-3.5-turbo\", api_key=\"your-key\")\nllm = OpenAI(model_name=\"gpt-4o\", api_key=\"your-key\")\n```\n\n**资料来源**：[datatune/llm/llm.py:64-73]()\n\n### 2. Ollama (本地部署)\n\n适用于本地运行的 LLM，通过 HTTP API 连接：\n\n```python\nfrom datatune.llm.llm import Ollama\n\nllm = Ollama()  # 默认 gemma3:4b，localhost:11434\nllm = Ollama(model_name=\"llama3:8b\", api_base=\"http://localhost:11434\")\n```\n\n**资料来源**：[datatune/llm/llm.py:53-61]()\n\n### 3. VLLM\n\nVLLM 服务器连接，支持 OpenAI 兼容 API：\n\n```python\nfrom datatune.llm.llm import VLLM\n\nllm = VLLM(\n    model_name=\"meta-llama/Llama-3-8B-Instruct\",\n    api_base=\"http://localhost:8000/v1\"\n)\n```\n\n**资料来源**：[datatune/llm/llm.py:75-91]()\n\n### 4. Azure OpenAI\n\nAzure 托管的 OpenAI 模型：\n\n```python\nfrom datatune.llm.llm import Azure\n\nllm = Azure(\n    model_name=\"gpt-3.5-turbo\",\n    api_key=\"your-key\",\n    api_base=\"https://xxx.openai.azure.com\",\n    api_version=\"2024-02-01\"\n)\n```\n\n**资料来源**：[datatune/llm/llm.py:93-106]()\n\n### 5. Gemini\n\nGoogle Gemini 模型集成：\n\n```python\nfrom datatune.llm.llm import Gemini\n\nllm = Gemini(model_name=\"gemini-1.5-pro\", api_key=\"your-key\")\n```\n\n### 6. Mistral\n\nMistral AI 模型：\n\n```python\nfrom datatune.llm.llm import Mistral\n\nllm = Mistral(model_name=\"mistral-tiny\")\nllm = Mistral(model_name=\"mistral-large-latest\")\n```\n\n**资料来源**：[datatune/llm/llm.py:130-148]()\n\n### 7. Huggingface\n\nHugging Face 推理端点：\n\n```python\nfrom datatune.llm.llm import Huggingface\n\nllm = Huggingface(model_name=\"meta-llama/Llama-3-8B-Instruct\")\n```\n\n**资料来源**：[datatune/llm/llm.py:150-172]()\n\n## 速率限制配置\n\n### 预定义模型限制\n\n`model_rate_limits.py` 文件包含各模型的默认速率限制：\n\n| 模型系列 | TPM (Token/分钟) | RPM (请求/分钟) |\n|----------|------------------|-----------------|\n| GPT-3.5-Turbo | 200,000 | 500 |\n| GPT-4 | 10,000 - 30,000 | 500 |\n| GPT-4.1 | 30,000 - 200,000 | 500 |\n| GPT-4o | 30,000 | 500 |\n| GPT-4.5-Preview | 125,000 | 1,000 |\n\n**资料来源**：[datatune/llm/model_rate_limits.py:1-85]()\n\n### 自定义速率限制\n\n可以在初始化时覆盖默认限制：\n\n```python\nfrom datatune.llm.llm import OpenAI\n\n# 自定义 RPM 和 TPM\nllm = OpenAI(\n    model_name=\"gpt-3.5-turbo\",\n    rpm=1000,\n    tpm=300000\n)\n```\n\n**资料来源**：[datatune/llm/llm.py:42-51]()\n\n## 使用方式\n\n### 基本调用\n\n所有 LLM 实例可作为可调用对象使用：\n\n```python\nllm = OpenAI(model_name=\"gpt-3.5-turbo\", api_key=\"sk-xxx\")\n\n# 单次调用\nresult = llm(\"Hello, how are you?\")\n```\n\n### 批处理调用\n\n对于大量数据处理，系统支持自动批处理：\n\n```python\nprompts = [\"Process row 1\", \"Process row 2\", \"Process row 3\"]\nresults = llm(prompts, prefix=\"Instruction: \", suffix=\"End.\")\n```\n\n**资料来源**：[datatune/llm/llm.py:85-105]()\n\n### 在 datatune 中的应用\n\ndatatune 的核心功能使用 LLM 集成进行数据转换：\n\n```python\nimport datatune as dt\nfrom datatune.llm.llm import OpenAI\n\nllm = OpenAI(model_name=\"gpt-3.5-turbo\")\n\n# 映射操作\nmapped = dt.map(\n    prompt=\"Extract categories from description\",\n    output_fields=[\"Category\"],\n    input_fields=[\"Description\"]\n)(llm, df)\n\n# 过滤操作\nfiltered = dt.filter(\n    prompt=\"Keep only electronics products\",\n    input_fields=[\"Name\"]\n)(llm, mapped)\n```\n\n**资料来源**：[README.md](https://github.com/vitalops/datatune/blob/main/README.md)\n\n## 核心方法\n\n### `_completion`\n\n执行单次补全请求：\n\n```python\ndef _completion(self, prompt: str) -> Union[str, Exception]:\n    messages = [{\"role\": \"user\", \"content\": prompt}]\n    from litellm import completion\n    \n    response = completion(model=self.model_name, messages=messages, **self.kwargs)\n    \n    if isinstance(response, Exception):\n        return response\n    return response[\"choices\"][0][\"message\"][\"content\"]\n```\n\n**资料来源**：[datatune/llm/llm.py:69-77]()\n\n### `get_max_tokens`\n\n获取模型支持的最大 token 数：\n\n```python\ndef get_max_tokens(self) -> int:\n    return get_max_tokens(self._base_model_name)\n```\n\n### `_create_batched_prompts`\n\n创建批处理提示词，用于优化多行数据处理：\n\n```python\ndef _create_batched_prompts(\n    self,\n    input_rows: List[str],\n    user_batch_prefix: str,\n    prompt_per_row: str,\n    batch_suffix: str = None\n) -> List[str]:\n    # 计算 token 限制并分批\n    # 返回批处理后的提示词列表\n```\n\n**资料来源**：[datatune/llm/llm.py:80-107]()\n\n## 依赖说明\n\nLLM 模块依赖 `litellm` 库实现底层 API 调用：\n\n```python\nfrom litellm import get_max_tokens, token_counter, batch_completion\n```\n\nlitellm 提供了对 100+ LLM 模型的统一接口，支持：\n- OpenAI 系列\n- Azure OpenAI\n- Anthropic\n- Google Vertex AI\n- AWS Bedrock\n- 本地部署模型 (Ollama, VLLM, TGI)\n- Hugging Face 推理端点\n\n**资料来源**：[datatune/llm/llm.py:10-12]()\n\n## 最佳实践\n\n### 1. 选择合适的模型\n\n| 场景 | 推荐模型 | 理由 |\n|------|----------|------|\n| 快速原型 | gpt-3.5-turbo | 成本低、速度快 |\n| 高质量输出 | gpt-4o | 性能与成本平衡 |\n| 最大精度 | gpt-4.1 | 最新最强模型 |\n| 本地部署 | Ollama/VLLM | 隐私保护、无 API 成本 |\n\n### 2. 配置合理的速率限制\n\n根据实际 API 配额设置：\n\n```python\n# 根据 API tier 设置\nllm = OpenAI(\n    model_name=\"gpt-4o\",\n    rpm=500,   # 根据配额调整\n    tpm=30000  # 根据配额调整\n)\n```\n\n### 3. 使用批处理优化\n\n对于大量数据处理，使用内置批处理功能避免单次调用限制：\n\n```python\n# 自动分批处理\nresults = llm(\n    input_rows,\n    prefix=\"任务: \",\n    suffix=\"请以指定格式输出\",\n    optimized=True  # 启用优化模式\n)\n```\n\n## 导出接口\n\n模块入口文件提供统一导出：\n\n```python\nfrom datatune.llm import *\n# 导出: LLM, Ollama, OpenAI, VLLM, Azure, Gemini, Mistral, Huggingface\n```\n\n**资料来源**：[datatune/llm/__init__.py]()\n\n## 总结\n\ndatatune 的 LLM 集成模块通过抽象化的设计，为用户提供了便捷的模型切换能力和统一的调用接口。开发者可以轻松地在不同 LLM 提供者之间切换，同时享受内置的速率限制保护和批处理优化功能。\n\n---\n\n<a id='datasource'></a>\n\n## 数据源支持\n\n### 相关页面\n\n相关主题：[系统架构](#architecture), [Map操作](#map-operation), [Filter操作](#filter-operation)\n\n<details>\n<summary>相关源码文件</summary>\n\n以下源码文件用于生成本页说明：\n\n- [datatune/llm/llm.py](https://github.com/vitalops/datatune/blob/main/datatune/llm/llm.py)\n- [datatune/agent/agent.py](https://github.com/vitalops/datatune/blob/main/datatune/agent/agent.py)\n- [datatune/core/dask/filter_dask.py](https://github.com/vitalops/datatune/blob/main/datatune/core/dask/filter_dask.py)\n- [datatune/core/dask/map_dask.py](https://github.com/vitalops/datatune/blob/main/datatune/core/dask/map_dask.py)\n- [datatune/core/ibis/filter_ibis.py](https://github.com/vitalops/datatune/blob/main/datatune/core/ibis/filter_ibis.py)\n- [datatune/core/ibis/map_ibis.py](https://github.com/vitalops/datatune/blob/main/datatune/core/ibis/map_ibis.py)\n- [datatune/agent/__init__.py](https://github.com/vitalops/datatune/blob/main/datatune/agent/__init__.py)\n</details>\n\n# 数据源支持\n\n## 概述\n\nDatatune 是一个数据处理框架，通过集成大语言模型（LLM）来实现智能化的数据转换和清洗。该框架的核心设计理念是支持多种数据源，使得用户能够在不同的计算后端上执行 `map`、`filter` 等数据转换操作。\n\n数据源支持架构采用插件式设计，通过抽象层将具体的数据处理逻辑与底层数据源解耦。当前框架主要支持两大类数据源：**Dask**（用于大规模并行计算）和 **Ibis**（支持多种 SQL 后端）。\n\n资料来源：[datatune/agent/__init__.py:1-5]()\n\n## 支持的数据源类型\n\nDatatune 支持的数据源可分为以下几类：\n\n| 数据源类型 | 底层引擎 | 特点 | 适用场景 |\n|-----------|---------|------|---------|\n| Dask DataFrame | Dask | 延迟计算、分布式执行、内存优化 | 大规模单机或集群数据处理 |\n| DuckDB | Ibis + DuckDB | 嵌入式 OLAP、零配置 | 快速分析、交互式查询 |\n| PostgreSQL | Ibis + PostgreSQL | 关系型数据库、企业级支持 | 生产环境、事务处理 |\n| BigQuery | Ibis + BigQuery | 云原生、超大规模 | 云端大数据分析 |\n\n资料来源：[README.md:1-50]()\n\n## 架构设计\n\n### 核心抽象层\n\nDatatune 的数据源支持通过统一的抽象接口实现，主要包含以下核心组件：\n\n```python\n# 抽象基类定义\nclass Agent(ABC):\n    system_prompt: str = \"\"\"You are Datatune Agent...\"\"\"\n```\n\n代理（Agent）类作为核心抽象，负责协调 LLM 与数据源的交互。不同的数据源实现继承该基类并实现具体的数据操作逻辑。\n\n资料来源：[datatune/agent/__init__.py:1-10]()\n\n### 数据流架构\n\n```mermaid\ngraph TD\n    A[用户代码] --> B[Datatune API]\n    B --> C{数据源类型}\n    C -->|Dask| D[core/dask/]\n    C -->|Ibis| E[core/ibis/]\n    D --> F[map_dask.py / filter_dask.py]\n    E --> G[map_ibis.py / filter_ibis.py]\n    F --> H[LLM 调用]\n    G --> H\n    H --> I[结果聚合]\n    I --> J[compute / execute]\n```\n\n## Dask 数据源\n\n### 概述\n\nDask 是 Datatune 默认支持的数据源，提供大规模并行计算能力。通过 Dask DataFrame，用户可以处理超出内存限制的数据集。\n\n### 主要操作\n\nDask 数据源支持两类核心操作：\n\n#### Map 操作\n\n用于从现有列生成新列，常用于语义提取、分类、自然语言推理等场景。\n\n```python\nimport datatune as dt\nimport dask.dataframe as dd\n\ndf = dd.read_csv(\"products.csv\")\n\nmapped = dt.map(\n    prompt=\"Extract categories from the description and name of product.\",\n    output_fields=[\"Category\", \"Subcategory\"],\n    input_fields=[\"Description\", \"Name\"]\n)(llm, df)\n```\n\n资料来源：[README.md:1-30]()\n\n#### Filter 操作\n\n用于根据条件过滤行，基于 LLM 理解保留符合条件的记录。\n\n```python\nfiltered = dt.filter(\n    prompt=\"Keep only electronics products\",\n    input_fields=[\"Name\"]\n)(llm, mapped)\n```\n\n资料来源：[README.md:1-35]()\n\n### Dask 实现细节\n\n#### 输出格式规范\n\nMap 和 Filter 操作要求 LLM 返回特定格式的输出：\n\n```\nindex=<row_index>|{key1: value1, key2: value2, ...}<endofrow>\n```\n\n- 字符串必须用双引号包裹\n- 缺失值设置为 `None` 或 `null`\n- 每行输出必须以 `<endofrow>` 结尾\n\n资料来源：[datatune/core/dask/map_dask.py:1-30]()\n\n#### 重复数据处理\n\nDask 实现包含智能去重机制，通过 `dup_to_canon` 映射处理重复数据：\n\n```python\ndup_to_canon = {\n    dup: c[\"canonical_id\"]\n    for c in clusters\n    for dup in c[\"duplicate_ids\"]\n}\n```\n\n只有规范索引的数据会被发送到 LLM，重复项的结果会自动复制。\n\n资料来源：[datatune/core/dask/map_dask.py:1-45]()\n\n## Ibis 数据源\n\n### 概述\n\nIbis 是一个 Python 抽象层，支持多种 SQL 后端。通过 Ibis，Datatune 可以连接到 DuckDB、PostgreSQL、BigQuery 等数据库执行向量化查询。\n\n### 连接方式\n\n#### DuckDB\n\n```python\nimport ibis\ncon = ibis.duckdb.connect(\"data.duckdb\")\ntable = con.table(\"my_table\")\n```\n\n资料来源：[README.md:1-60]()\n\n#### PostgreSQL\n\n```python\nimport ibis\ncon = ibis.postgres.connect(...)\ntable = con.table(\"my_table\")\n```\n\n#### BigQuery\n\n```python\nimport ibis\ncon = ibis.bigquery.connect(...)\ntable = con.table(\"my_table\")\n```\n\n### Ibis 实现特点\n\n#### 行号索引\n\nIbis 实现使用 `ibis.row_number()` 生成行索引：\n\n```python\nindexed_table = table.mutate(_ROW_ID_=ibis.row_number().cast(\"int64\"))\nlocal_data = indexed_table.select(\"_ROW_ID_\", input_col).execute()\n```\n\n资料来源：[datatune/core/ibis/filter_ibis.py:1-40]()\n\n#### 输出解析\n\nIbis 后端将 LLM 输出解析为布尔值用于过滤：\n\n```python\nfor res in results:\n    try:\n        d = ast.literal_eval(str(res).strip())\n        if d:\n            last_key = list(d.keys())[-1]\n            flag = d[last_key]\n            out_bools.append(flag)\n    except Exception:\n        out_bools.append(False)\n```\n\n资料来源：[datatune/core/ibis/filter_ibis.py:1-60]()\n\n## Agent 自动化\n\n### 功能概述\n\nDatatune Agent 可以自动识别数据转换需求，智能选择合适的操作类型并执行。\n\n```python\nimport datatune as dt\nfrom datatune.llm.llm import OpenAI\n\nllm = OpenAI(model_name=\"gpt-3.5-turbo\")\nagent = dt.Agent(llm)\n\ndf = agent.do(\"Add ProfitMargin column and keep only African organizations\", df)\n```\n\n资料来源：[README.md:1-45]()\n\n### 计划生成规则\n\nAgent 生成执行计划时遵循以下规则：\n\n1. **Dask 操作选择**：如果任务涉及添加列、分组、移位、按行或按列应用函数，使用 `type: \"dask\"`\n2. **Primitive 操作选择**：如果需要理解自然语言、提取或解释文本内容、进行语义推理，使用 `type: \"primitive\"`\n3. **简化优先**：能用 Dask 完成的步骤优先使用 Dask，只有 Dask 无法实现时才使用 primitives\n\n资料来源：[datatune/agent/agent.py:1-100]()\n\n### 支持的操作类型\n\n| 操作类型 | 说明 | 适用场景 |\n|---------|------|---------|\n| add_column | 从表达式创建新列 | 数值计算、日期提取 |\n| apply_function | 对单列应用函数 | 逐元素转换 |\n| rename_columns | 重命名列 | 列名规范化 |\n| astype_column | 更改列数据类型 | 类型转换 |\n| group_by_agg | 分组聚合 | 统计计算 |\n| conditional_column | 条件列 | 基于条件的新列 |\n\n资料来源：[datatune/agent/agent.py:1-150]()\n\n## LLM 集成\n\n### 支持的模型提供商\n\nDatatune 通过 litellm 库支持多种 LLM 提供商：\n\n| 提供商 | 类名 | 默认模型 | 说明 |\n|-------|------|---------|------|\n| OpenAI | `OpenAI` | gpt-3.5-turbo | OpenAI GPT 系列 |\n| Ollama | `Ollama` | gemma3:4b | 本地模型支持 |\n| vLLM | `VLLM` | 自定义 | 高性能推理 |\n| Azure | `Azure` | 自定义 | Azure OpenAI 服务 |\n\n资料来源：[datatune/llm/llm.py:1-100]()\n\n### 模型初始化示例\n\n```python\n# OpenAI\nfrom datatune.llm.llm import OpenAI\nllm = OpenAI(model_name=\"gpt-3.5-turbo\")\n\n# Ollama (本地)\nfrom datatune.llm.llm import Ollama\nllm = Ollama()\n\n# Azure\nfrom datatune.llm.llm import Azure\nllm = Azure(model_name=\"gpt-3.5-turbo\", api_key=api_key)\n```\n\n资料来源：[README.md:1-70]()\n\n### 速率限制配置\n\n框架内置了主要模型的速率限制配置：\n\n```python\nmodel_rate_limits = {\n    \"gpt-3.5-turbo\": {\n        \"tpm\": 200_000,  # tokens per minute\n        \"rpm\": 500,      # requests per minute\n    },\n    \"gpt-4\": {\n        \"tpm\": 10_000,\n        \"rpm\": 500,\n    },\n    # ...\n}\n```\n\n资料来源：[datatune/llm/model_rate_limits.py:1-50]()\n\n## 使用工作流\n\n### 完整数据处理流程\n\n```mermaid\ngraph LR\n    A[加载数据] --> B[定义 LLM]\n    B --> C[应用 Map]\n    C --> D[应用 Filter]\n    D --> E[执行 Finalize]\n    E --> F[保存结果]\n```\n\n### 代码示例\n\n```python\nimport datatune as dt\nfrom datatune.llm.llm import OpenAI\nimport dask.dataframe as dd\n\n# 初始化\nllm = OpenAI(model_name=\"gpt-3.5-turbo\")\ndf = dd.read_csv(\"products.csv\")\n\n# 映射操作\nmapped = dt.map(\n    prompt=\"Extract categories from the description and name of product.\",\n    output_fields=[\"Category\", \"Subcategory\"],\n    input_fields=[\"Description\", \"Name\"]\n)(llm, df)\n\n# 过滤操作\nfiltered = dt.filter(\n    prompt=\"Keep only electronics products\",\n    input_fields=[\"Name\"]\n)(llm, mapped)\n\n# 最终计算并保存\nresult = dt.finalize(filtered)\nresult.compute().to_csv(\"electronics_products.csv\")\n```\n\n资料来源：[README.md:1-40]()\n\n## 性能优化\n\n### 去重处理\n\n对于包含重复行的数据集，框架采用智能去重策略：\n\n1. 使用 embedding 模型识别语义相似的记录\n2. 将相似记录聚类，只对规范记录调用 LLM\n3. 将结果自动复制到同簇的其他记录\n\n```python\nclass Deduplicator:\n    def __init__(\n        self,\n        embedding_model: str = \"text-embedding-3-small\",\n        sim_threshold: float = 0.90,\n        top_k: int = 50,\n    ):\n        # ...\n```\n\n资料来源：[datatune/core/deduplication.py:1-30]()\n\n### 分区处理\n\nDask 数据源支持分区并行处理：\n\n```python\nfor pid in range(df.npartitions):\n    part = df[column].get_partition(pid)\n    task = dask.delayed(self._embed_and_write_partition)(part, pid, output_dir)\n```\n\n资料来源：[datatune/core/deduplication.py:1-80]()\n\n## 注意事项\n\n1. **数据量控制**：LLM 调用按批次进行，需注意 token 限制\n2. **输出格式**：严格遵循 `index=<index>|{...}<endofrow>` 格式\n3. **字符串转义**：输出必须使用双引号，避免裸文本\n4. **错误处理**：单个记录解析失败不影响整体流程\n5. **资源限制**：生产环境建议配置合理的 `tpm` 和 `rpm` 参数\n\n---\n\n---\n\n## Doramagic 踩坑日志\n\n项目：vitalops/datatune\n\n摘要：发现 7 个潜在踩坑项，其中 0 个为 high/blocking；最高优先级：能力坑 - 能力判断依赖假设。\n\n## 1. 能力坑 · 能力判断依赖假设\n\n- 严重度：medium\n- 证据强度：source_linked\n- 发现：README/documentation is current enough for a first validation pass.\n- 对用户的影响：假设不成立时，用户拿不到承诺的能力。\n- 建议检查：将假设转成下游验证清单。\n- 防护动作：假设必须转成验证项；没有验证结果前不能写成事实。\n- 证据：capability.assumptions | art_fcb598a7a7ed4b2482ca92474f06efe2 | https://github.com/vitalops/datatune#readme | README/documentation is current enough for a first validation pass.\n\n## 2. 维护坑 · 维护活跃度未知\n\n- 严重度：medium\n- 证据强度：source_linked\n- 发现：未记录 last_activity_observed。\n- 对用户的影响：新项目、停更项目和活跃项目会被混在一起，推荐信任度下降。\n- 建议检查：补 GitHub 最近 commit、release、issue/PR 响应信号。\n- 防护动作：维护活跃度未知时，推荐强度不能标为高信任。\n- 证据：evidence.maintainer_signals | art_fcb598a7a7ed4b2482ca92474f06efe2 | https://github.com/vitalops/datatune#readme | last_activity_observed missing\n\n## 3. 安全/权限坑 · 下游验证发现风险项\n\n- 严重度：medium\n- 证据强度：source_linked\n- 发现：no_demo\n- 对用户的影响：下游已经要求复核，不能在页面中弱化。\n- 建议检查：进入安全/权限治理复核队列。\n- 防护动作：下游风险存在时必须保持 review/recommendation 降级。\n- 证据：downstream_validation.risk_items | art_fcb598a7a7ed4b2482ca92474f06efe2 | https://github.com/vitalops/datatune#readme | no_demo; severity=medium\n\n## 4. 安全/权限坑 · 存在安全注意事项\n\n- 严重度：medium\n- 证据强度：source_linked\n- 发现：No sandbox install has been executed yet; downstream must verify before user use.\n- 对用户的影响：用户安装前需要知道权限边界和敏感操作。\n- 建议检查：转成明确权限清单和安全审查提示。\n- 防护动作：安全注意事项必须面向用户前置展示。\n- 证据：risks.safety_notes | art_fcb598a7a7ed4b2482ca92474f06efe2 | https://github.com/vitalops/datatune#readme | No sandbox install has been executed yet; downstream must verify before user use.\n\n## 5. 安全/权限坑 · 存在评分风险\n\n- 严重度：medium\n- 证据强度：source_linked\n- 发现：no_demo\n- 对用户的影响：风险会影响是否适合普通用户安装。\n- 建议检查：把风险写入边界卡，并确认是否需要人工复核。\n- 防护动作：评分风险必须进入边界卡，不能只作为内部分数。\n- 证据：risks.scoring_risks | art_fcb598a7a7ed4b2482ca92474f06efe2 | https://github.com/vitalops/datatune#readme | no_demo; severity=medium\n\n## 6. 维护坑 · issue/PR 响应质量未知\n\n- 严重度：low\n- 证据强度：source_linked\n- 发现：issue_or_pr_quality=unknown。\n- 对用户的影响：用户无法判断遇到问题后是否有人维护。\n- 建议检查：抽样最近 issue/PR，判断是否长期无人处理。\n- 防护动作：issue/PR 响应未知时，必须提示维护风险。\n- 证据：evidence.maintainer_signals | art_fcb598a7a7ed4b2482ca92474f06efe2 | https://github.com/vitalops/datatune#readme | issue_or_pr_quality=unknown\n\n## 7. 维护坑 · 发布节奏不明确\n\n- 严重度：low\n- 证据强度：source_linked\n- 发现：release_recency=unknown。\n- 对用户的影响：安装命令和文档可能落后于代码，用户踩坑概率升高。\n- 建议检查：确认最近 release/tag 和 README 安装命令是否一致。\n- 防护动作：发布节奏未知或过期时，安装说明必须标注可能漂移。\n- 证据：evidence.maintainer_signals | art_fcb598a7a7ed4b2482ca92474f06efe2 | https://github.com/vitalops/datatune#readme | release_recency=unknown\n\n<!-- canonical_name: vitalops/datatune; human_manual_source: deepwiki_human_wiki -->\n",
      "summary": "DeepWiki/Human Wiki 完整输出，末尾追加 Discovery Agent 踩坑日志。",
      "title": "Human Manual / 人类版说明书"
    },
    "pitfall_log": {
      "asset_id": "pitfall_log",
      "filename": "PITFALL_LOG.md",
      "markdown": "# Pitfall Log / 踩坑日志\n\n项目：vitalops/datatune\n\n摘要：发现 7 个潜在踩坑项，其中 0 个为 high/blocking；最高优先级：能力坑 - 能力判断依赖假设。\n\n## 1. 能力坑 · 能力判断依赖假设\n\n- 严重度：medium\n- 证据强度：source_linked\n- 发现：README/documentation is current enough for a first validation pass.\n- 对用户的影响：假设不成立时，用户拿不到承诺的能力。\n- 建议检查：将假设转成下游验证清单。\n- 防护动作：假设必须转成验证项；没有验证结果前不能写成事实。\n- 证据：capability.assumptions | art_fcb598a7a7ed4b2482ca92474f06efe2 | https://github.com/vitalops/datatune#readme | README/documentation is current enough for a first validation pass.\n\n## 2. 维护坑 · 维护活跃度未知\n\n- 严重度：medium\n- 证据强度：source_linked\n- 发现：未记录 last_activity_observed。\n- 对用户的影响：新项目、停更项目和活跃项目会被混在一起，推荐信任度下降。\n- 建议检查：补 GitHub 最近 commit、release、issue/PR 响应信号。\n- 防护动作：维护活跃度未知时，推荐强度不能标为高信任。\n- 证据：evidence.maintainer_signals | art_fcb598a7a7ed4b2482ca92474f06efe2 | https://github.com/vitalops/datatune#readme | last_activity_observed missing\n\n## 3. 安全/权限坑 · 下游验证发现风险项\n\n- 严重度：medium\n- 证据强度：source_linked\n- 发现：no_demo\n- 对用户的影响：下游已经要求复核，不能在页面中弱化。\n- 建议检查：进入安全/权限治理复核队列。\n- 防护动作：下游风险存在时必须保持 review/recommendation 降级。\n- 证据：downstream_validation.risk_items | art_fcb598a7a7ed4b2482ca92474f06efe2 | https://github.com/vitalops/datatune#readme | no_demo; severity=medium\n\n## 4. 安全/权限坑 · 存在安全注意事项\n\n- 严重度：medium\n- 证据强度：source_linked\n- 发现：No sandbox install has been executed yet; downstream must verify before user use.\n- 对用户的影响：用户安装前需要知道权限边界和敏感操作。\n- 建议检查：转成明确权限清单和安全审查提示。\n- 防护动作：安全注意事项必须面向用户前置展示。\n- 证据：risks.safety_notes | art_fcb598a7a7ed4b2482ca92474f06efe2 | https://github.com/vitalops/datatune#readme | No sandbox install has been executed yet; downstream must verify before user use.\n\n## 5. 安全/权限坑 · 存在评分风险\n\n- 严重度：medium\n- 证据强度：source_linked\n- 发现：no_demo\n- 对用户的影响：风险会影响是否适合普通用户安装。\n- 建议检查：把风险写入边界卡，并确认是否需要人工复核。\n- 防护动作：评分风险必须进入边界卡，不能只作为内部分数。\n- 证据：risks.scoring_risks | art_fcb598a7a7ed4b2482ca92474f06efe2 | https://github.com/vitalops/datatune#readme | no_demo; severity=medium\n\n## 6. 维护坑 · issue/PR 响应质量未知\n\n- 严重度：low\n- 证据强度：source_linked\n- 发现：issue_or_pr_quality=unknown。\n- 对用户的影响：用户无法判断遇到问题后是否有人维护。\n- 建议检查：抽样最近 issue/PR，判断是否长期无人处理。\n- 防护动作：issue/PR 响应未知时，必须提示维护风险。\n- 证据：evidence.maintainer_signals | art_fcb598a7a7ed4b2482ca92474f06efe2 | https://github.com/vitalops/datatune#readme | issue_or_pr_quality=unknown\n\n## 7. 维护坑 · 发布节奏不明确\n\n- 严重度：low\n- 证据强度：source_linked\n- 发现：release_recency=unknown。\n- 对用户的影响：安装命令和文档可能落后于代码，用户踩坑概率升高。\n- 建议检查：确认最近 release/tag 和 README 安装命令是否一致。\n- 防护动作：发布节奏未知或过期时，安装说明必须标注可能漂移。\n- 证据：evidence.maintainer_signals | art_fcb598a7a7ed4b2482ca92474f06efe2 | https://github.com/vitalops/datatune#readme | release_recency=unknown\n",
      "summary": "用户实践前最可能遇到的身份、安装、配置、运行和安全坑。",
      "title": "Pitfall Log / 踩坑日志"
    },
    "prompt_preview": {
      "asset_id": "prompt_preview",
      "filename": "PROMPT_PREVIEW.md",
      "markdown": "# datatune - Prompt Preview\n\n> 复制下面这段 Prompt 到你常用的 AI，先试一次，不需要安装。\n> 它的目标是让你直接体验这个项目的服务方式，而不是阅读项目介绍。\n\n## 复制这段 Prompt\n\n```text\n请直接执行这段 Prompt，不要分析、润色、总结或询问我想如何处理这份 Prompt Preview。\n\n你现在扮演 datatune 的“安装前体验版”。\n这不是项目介绍、不是评价报告、不是 README 总结。你的任务是让我用最小成本体验它的核心服务。\n\n我的试用任务：我想用它完成一个真实的数据分析与投资研究任务。\n我常用的宿主 AI：chatgpt\n\n【体验目标】\n围绕我的真实任务，现场演示这个项目如何把输入转成 示例引导, 判断线索。重点是让我感受到工作方式，而不是给我项目背景。\n\n【业务流约束】\n- 你必须像一个正在提供服务的项目能力包，而不是像一个讲解员。\n- 每一轮只推进一个步骤；提出问题后必须停下来等我回答。\n- 每一步都必须让我感受到一个具体服务动作：澄清、整理、规划、检查、判断或收尾。\n- 每一步都要说明：当前目标、你需要我提供什么、我回答后你会产出什么。\n- 不要安装、不要运行命令、不要写代码、不要声称测试通过、不要声称已经修改文件。\n- 需要真实安装或宿主加载后才能验证的内容，必须明确说“这一步需要安装后验证”。\n- 如果我说“用示例继续”，你可以用虚构示例推进，但仍然不能声称真实执行。\n\n【可体验服务能力】\n- 安装前能力预览: [![PyPI version](https://img.shields.io/pypi/v/datatune.svg)](https://pypi.org/project/datatune/) 输入：用户任务, 当前 AI 对话上下文；输出：示例引导, 判断线索。\n\n【必须安装后才可验证的能力】\n- 命令行启动或安装流程: 项目文档中存在可执行命令，真实使用需要在本地或宿主环境中运行这些命令。 输入：终端环境, 包管理器, 项目依赖；输出：安装结果, 列表/更新/运行结果。\n\n【核心服务流】\n请严格按这个顺序带我体验。不要一次性输出完整流程：\n1. intro：Datatune简介。围绕“Datatune简介”模拟一次用户任务，不展示安装或运行结果。\n2. quickstart：快速开始。围绕“快速开始”模拟一次用户任务，不展示安装或运行结果。\n3. architecture：系统架构。围绕“系统架构”模拟一次用户任务，不展示安装或运行结果。\n4. map-operation：Map操作。围绕“Map操作”模拟一次用户任务，不展示安装或运行结果。\n5. filter-operation：Filter操作。围绕“Filter操作”模拟一次用户任务，不展示安装或运行结果。\n\n【核心能力体验剧本】\n每一步都必须按“输入 -> 服务动作 -> 中间产物”执行。不要只说流程名：\n1. intro\n输入：用户提供的“Datatune简介”相关信息。\n服务动作：模拟项目在这一步的核心判断和整理方式。\n中间产物：一个可检查的小结果。\n\n2. quickstart\n输入：用户提供的“快速开始”相关信息。\n服务动作：模拟项目在这一步的核心判断和整理方式。\n中间产物：一个可检查的小结果。\n\n3. architecture\n输入：用户提供的“系统架构”相关信息。\n服务动作：模拟项目在这一步的核心判断和整理方式。\n中间产物：一个可检查的小结果。\n\n4. map-operation\n输入：用户提供的“Map操作”相关信息。\n服务动作：模拟项目在这一步的核心判断和整理方式。\n中间产物：一个可检查的小结果。\n\n5. filter-operation\n输入：用户提供的“Filter操作”相关信息。\n服务动作：模拟项目在这一步的核心判断和整理方式。\n中间产物：一个可检查的小结果。\n\n【项目服务规则】\n这些规则决定你如何服务用户。不要解释规则本身，而要在每一步执行时遵守：\n- 先确认用户任务、输入材料和成功标准，再模拟项目能力。\n- 每一步都必须形成可检查的小产物，并等待用户确认后再继续。\n- 凡是需要安装、调用工具或访问外部服务的能力，都必须标记为安装后验证。\n\n【每一步的服务约束】\n- Step 1 / intro：Step 1 必须围绕“Datatune简介”形成一个小中间产物，并等待用户确认。\n- Step 2 / quickstart：Step 2 必须围绕“快速开始”形成一个小中间产物，并等待用户确认。\n- Step 3 / architecture：Step 3 必须围绕“系统架构”形成一个小中间产物，并等待用户确认。\n- Step 4 / map-operation：Step 4 必须围绕“Map操作”形成一个小中间产物，并等待用户确认。\n- Step 5 / filter-operation：Step 5 必须围绕“Filter操作”形成一个小中间产物，并等待用户确认。\n\n【边界与风险】\n- 不要声称已经安装、运行、调用 API、读写本地文件或完成真实任务。\n- 安装前预览只能展示工作方式，不能证明兼容性、性能或输出质量。\n- 涉及安装、插件加载、工具调用或外部服务的能力必须安装后验证。\n\n【可追溯依据】\n这些路径只用于你内部校验或在我追问“依据是什么”时简要引用。不要在首次回复主动展开：\n- https://github.com/vitalops/datatune#readme\n- README.md\n- datatune/__init__.py\n- pyproject.toml\n- examples/Getting_started.ipynb\n- datatune/agent/agent.py\n- datatune/agent/runtime.py\n- datatune/core/map.py\n- datatune/core/filter.py\n- datatune/core/reduce.py\n- datatune/core/dask/map_dask.py\n- datatune/core/ibis/map_ibis.py\n\n【首次问题规则】\n- 首次三问必须先确认用户目标、成功标准和边界，不要提前进入工具、安装或实现细节。\n- 如果后续需要技术条件、文件路径或运行环境，必须等用户确认目标后再追问。\n\n首次回复必须只输出下面 4 个部分：\n1. 体验开始：用 1 句话说明你将带我体验 datatune 的核心服务。\n2. 当前步骤：明确进入 Step 1，并说明这一步要解决什么。\n3. 你会如何服务我：说明你会先改变我完成任务的哪个动作。\n4. 只问我 3 个问题，然后停下等待回答。\n\n首次回复禁止输出：后续完整流程、证据清单、安装命令、项目评价、营销文案、已经安装或运行的说法。\n\nStep 1 / brainstorming 的二轮协议：\n- 我回答首次三问后，你仍然停留在 Step 1 / brainstorming，不要进入 Step 2。\n- 第二次回复必须产出 6 个部分：澄清后的任务定义、成功标准、边界条件、\n  2-3 个可选方案、每个方案的权衡、推荐方案。\n- 第二次回复最后必须问我是否确认推荐方案；只有我明确确认后，才能进入下一步。\n- 第二次回复禁止输出 git worktree、代码计划、测试文件、命令或真实执行结果。\n\n后续对话规则：\n- 我回答后，你先完成当前步骤的中间产物并等待确认；只有我确认后，才能进入下一步。\n- 每一步都要生成一个小的中间产物，例如澄清后的目标、计划草案、测试意图、验证清单或继续/停止判断。\n- 所有演示都写成“我会建议/我会引导/这一步会形成”，不要写成已经真实执行。\n- 不要声称已经测试通过、文件已修改、命令已运行或结果已产生。\n- 如果某个能力必须安装后验证，请直接说“这一步需要安装后验证”。\n- 如果证据不足，请明确说“证据不足”，不要补事实。\n```\n",
      "summary": "不安装项目也能感受能力节奏的安全试用 Prompt。",
      "title": "Prompt Preview / 安装前试用 Prompt"
    },
    "quick_start": {
      "asset_id": "quick_start",
      "filename": "QUICK_START.md",
      "markdown": "# Quick Start / 官方入口\n\n项目：vitalops/datatune\n\n## 官方安装入口\n\n### Python / pip · 官方安装入口\n\n```bash\npip install datatune\n```\n\n来源：https://github.com/vitalops/datatune#readme\n\n## 来源\n\n- docs: https://github.com/vitalops/datatune#readme\n",
      "summary": "从项目官方 README 或安装文档提取的开工入口。",
      "title": "Quick Start / 官方入口"
    }
  },
  "validation_id": "dval_aaf5c46de35c48cba1717ee2b6667f9a"
}
