ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

基于 agno 的文本 Span 标注实战:命名实体识别与 PII 脱敏

基于 agno 的文本 Span 标注实战:命名实体识别与 PII 脱敏 基于 agno 的文本 Span 标注实战命名实体识别与 PII 脱敏【免费下载链接】agnoBuild, run, and manage agent platforms.项目地址: https://gitcode.com/GitHub_Trending/ag/agno文本 Span 标注Text Span Labeling是数据标注流水线中最常见也最容易做错的场景之一让 LLM 找出文本中一段打了标签的子串。本文基于 agno 官方 cookbook 的_04_text_span_labeling示例讲解其核心模式——让模型返回原文子串、用 Python 计算字符偏移并给出两套可运行实现通用命名实体识别NER与 PII 检测脱敏。读完本文你将掌握如何用 agno 的output_schema约束模型输出、如何规避让 LLM 数字符这一经典陷阱以及如何把标注结果应用到实际下游流程。一、为什么 Span 标注不能靠模型数字符Span 标注的目标是给定一段文本找出所有打了标签的子串输出一组(text, label)对。很多初学者会直接要求模型返回第 5 个字符到第 12 个字符之类的偏移量。这是不可靠的——LLM 在字符级别的计数上经常出错。正确的做法也是本目录两处示例统一采用的模式是让模型返回原文中的精确子串保留大小写与标点及其标签由 Python 在后处理阶段用text.find()定位子串从而得到字符偏移。这一点在 README.md 中被明确强调Asking the LLM to count characters is unreliable - the right shape is to have the model return the literal substring and let Python locate it.在 basic.py 的文件头注释中同样重申了这一设计原则。这种模型出文本、代码算位置的分工把不可靠的算术交给确定性的 Python把语义理解留给模型是生产环境中鲁棒性最高的一种标注形态。二、示例文件概览与适用场景目录cookbook/data_labeling/_04_text_span_labeling/下包含两个可独立运行的示例文件功能basic.py实体 Span 检测标签集合为PERSON、ORG、LOCATION、DATEpii_redaction.pyPII Span 检测email、phone、ssn、credit_card、person_name外加一个简单的脱敏步骤pii_redaction.py与basic.py共享同一套核心模式模型只负责返回精确子串和类型Python 负责find-and-replace式的后处理。典型适用场景对客服工单做 NER抽取实体以构建客户联系关系图contact graph在存储用户输入之前做 PII 检测避免敏感信息入库在长文档中高亮标注论点claims或证据evidence。与其他数据标注 cookbook 的定位区分如果你需要的不是子串 标签的 Span而是一个结构化的类型化对象应该改用_03_text_extraction/中的文本抽取示例把自由文本抽成受你控制的 Pydantic 对象如果只是对整段文本打一个标签则参考_01_text_classification/。本目录在整个cookbook/data_labeling/体系中属于按输出形态划分的 span-label 一类与 classify、extract、rank 三类并列。三、运行环境与前置条件示例默认使用google:gemini-3.5-flash模型因此运行前需要配置GOOGLE_API_KEY环境变量。在 agno 仓库根目录下执行python cookbook/data_labeling/_04_text_span_labeling/basic.py python cookbook/data_labeling/_04_text_span_labeling/pii_redaction.py两个脚本均无额外第三方依赖只用到agno、pydantic和rich可直接端到端运行。若要按 cookbook 的标准流程搭建演示环境可先执行 scripts/demo_setup.sh 创建并激活 demo 虚拟环境再运行上述命令。四、示例一NER 实体 Span 检测basic.pybasic.py 完整演示了模型返回子串 Python 计算偏移的全流程。4.1 定义输出 Schema首先是两个 Pydantic 模型Entity描述单个实体Entities包装实体列表from typing import List, Literal from pydantic import BaseModel, Field class Entity(BaseModel): text: str Field(..., descriptionExact substring from the input) label: Literal[PERSON, ORG, LOCATION, DATE] Field( ..., descriptionEntity type ) class Entities(BaseModel): entities: List[Entity]注意两个关键设计text字段的 description 明确要求模型返回输入中的精确子串label使用Literal[PERSON, ORG, LOCATION, DATE]将标签限定在固定枚举内保证输出可消费。4.2 编写指令Instructions指令是 Span 标注质量的关键必须把精确匹配的约束写清楚instructions \ Extract all named entities from the input. For each entity, return the exact substring as it appears in the text (case and punctuation preserved) along with its label. Do not paraphrase or normalize. Do not include pronouns or generic references. 要点有三返回原样子串保留大小写与标点、不要改写或规范化、排除代词和泛指引用如 the customer。最后一条对 NER 的召回精度影响很大。4.3 创建 Agent通过output_schema把结构化约束交给 agnofrom agno.agent import Agent agent Agent( modelgoogle:gemini-3.5-flash, instructionsinstructions, output_schemaEntities, )在 agno 源码中output_schema是 Agent 的核心结构化输出参数可接受Type[BaseModel]或Dict[str, Any]见 libs/agno/agno/agent/agent.py。与之配套的参数还包括parse_response: bool True——为 True 时模型响应会被解析转换为output_schema实例否则仅返回 JSON 字符串structured_outputs: Optional[bool]——模型支持时启用强制结构化输出如 OpenAI 系use_json_mode: bool False——在 system message 中注入输出 schema 的 JSON 描述而非直接传 Pydantic schema。对本示例而言默认的parse_responseTrue意味着run.content将直接是Entities实例即run.content.entities就是实体列表。4.4 后处理用 text.find() 计算偏移核心的偏移计算函数def with_positions(text: str, entities: List[Entity]): Find each entitys first occurrence offset; useful for downstream tagging. for e in entities: start text.find(e.text) end start len(e.text) if start 0 else None yield {label: e.label, text: e.text, start: start, end: end}逻辑非常直接start用str.find()找第一个出现位置end由start len(text)推导。注意start可能为-1未找到时的防御处理。这个生成器返回的{label, text, start, end}字典天然适合下游的标注、高亮或序列标注BIO 标签转换。主流程与示例输入from rich.pretty import pprint from agno.agent import RunOutput if __name__ __main__: text ( On March 3rd, Sarah Johnson left Acme Corp to join a startup based in Berlin called Lumen Labs. ) run: RunOutput agent.run(text) pprint(list(with_positions(text, run.content.entities)))RunOutput是Agent.run()/Workflow.run()的返回值类型定义见 libs/agno/agno/run/agent.pycontent字段在parse_responseTrue时即为 schema 实例。上例输入中包含日期、人名、两个机构和一个地点是 NER 的经典短句。4.5 预期输出来自测试日志TEST_LOG.md 记录了 2026-07-18 在gemini-3.5-flash、agno 2.7.4 下的实测结果PASSDATE March 3rd偏移 3-12PERSON Sarah Johnson偏移 14-27ORG Acme Corp偏移 33-42LOCATION Berlin偏移 70-76ORG Lumen Labs偏移 84-94共返回 5 个实体且偏移全部正确单次运行约 2.9s。注意这些偏移完全由 Python 计算模型只负责给出子串这正是本模式鲁棒性的实证。五、示例二PII 检测与脱敏pii_redaction.pypii_redaction.py 是同一模式在隐私场景的延伸模型返回 PII 精确子串Python 执行查找并替换完成脱敏。5.1 定义 PII Schemaclass PIIItem(BaseModel): text: str Field(..., descriptionExact substring containing the PII) type: Literal[email, phone, ssn, credit_card, person_name] class PIIDetection(BaseModel): items: List[PIIItem]标签枚举覆盖最常见的五类 PII邮箱、电话、SSN、信用卡号、人名。5.2 指令检测与脱敏职责分离instructions \ Detect every span of personally identifiable information. For each item, return the exact substring (preserving case and formatting) and its type. Do not redact in your output - emit the raw text. Redaction happens in post-processing. Cover: email addresses, phone numbers, SSNs, credit card numbers, and full person names. Skip generic references like the customer. 指令中有一条容易被忽略但极其重要的约束Do not redact in your output - emit the raw text. Redaction happens in post-processing.即模型只管检测绝不脱敏脱敏一律交给确定性代码。这样既能验证模型检测的正确性又保证替换逻辑 100% 可复现避免模型擅自改写破坏原文。5.3 后处理脱敏函数def redact(text: str, items: List[PIIItem]) - str: Replace each detected span with a token of the form [TYPE]. out text for item in items: out out.replace(item.text, f[{item.type.upper()}]) return out对每个检测到的 span用str.replace替换为[TYPE]形式的令牌如[EMAIL]、[PHONE]。主流程与示例输入if __name__ __main__: text ( Customer Jane Doe called from 415-555-0199 about her order. She asked us to email jane.doeexample.com with the receipt. Card on file ends 4242 4242 4242 4242. ) run: RunOutput agent.run(text) detection run.content pprint({detected: detection.items, redacted: redact(text, detection.items)})5.4 预期输出来自测试日志同一份 TEST_LOG.md 记录的实测结果PASS四个 PII 项全部被正确检测person_name Jane Doephone 415-555-0199email jane.doeexample.comcredit_card 4242 4242 4242 4242脱敏后的输出为Customer [PERSON_NAME] called from [PHONE] about her order. She asked us to email [EMAIL] with the receipt. Card on file ends [CREDIT_CARD].单次运行同样约 2.9s。注意示例输入中的 the customer 等泛指表达被指令明确排除未被误报。六、模式提炼何时使用 Span 标注综合两个示例可以提炼出本目录反复强调的模式要点输出形态是(text, label)对而不是结构化字段或整体分类——当下游真正需要哪一段文本、是什么类型时Span 标注是最贴合的形态模型只负责语义判断Python 负责几何计算子串定位、偏移推导、替换脱敏全部后置避免模型在不擅长的字符计数上犯错Schema 用Literal锁定标签集合保证下游可以直接消费无需处理自由文本标签指令中显式声明不要泛指、不要改写是召回精度与匹配成功率的保障当场景变成只要一个类型化对象、不要偏移时改用_03_text_extraction/中的抽取模式避免为简单需求引入偏移计算。这一模式在生产中可以无缝扩展到更多场景如高亮长文档中的论点与证据(text, label)直接驱动 UI 高亮、把客服工单实体抽取结果灌入联系图谱、或作为序列标注任务的前置数据增强。若需要大规模并行标注可参考 data_labeling 体系中的_26_scale_out/异步扇出与断点续跑机制若需要质检标注质量可叠加_18_quality_review/的标注者-审核者-裁决者流水线。七、小结本文完整拆解了 agno cookbook_04_text_span_labeling的两个可运行示例基于output_schema Pydantic 约束的 NER 实体 Span 检测以及同样机制下的 PII 检测与[TYPE]令牌脱敏。核心方法论只有一句话让模型返回精确子串用 Python 的text.find()计算偏移、用str.replace()执行脱敏。这一模式规避了 LLM 字符计数不可靠的根本缺陷且代码路径全部确定、可测试、可审计值得在各类数据标注与隐私合规场景中直接复用。【免费下载链接】agnoBuild, run, and manage agent platforms.项目地址: https://gitcode.com/GitHub_Trending/ag/agno创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表