假设你问个人资料助手帮我查一下保存的读书会通知报名截止日期是哪天要回答这个问题助手先要找到相应的通知。我们给它提供了一个名叫search_notices的工具意思是“搜索通知”对应程序里实际搜索已保存通知的函数。程序把用户问题交给模型时也会提供工具说明这个工具能搜索已保存通知调用时需要搜索关键词还可以限定通知的发布日期。模型根据用户问题和这些说明判断是否请求使用它。这里要核对的是通知中的报名日期因此搜索通知是合适的操作。模型请求使用这个工具时需要把搜索条件交给程序。例如下面是一份参数示例{query:读书会 报名,published_from:null}这里query是搜索关键词published_from用来限定通知的发布日期null表示这次不加日期限制。接下来程序要把这份请求变成真正的搜索。执行前需要确认工具名是否在允许范围内参数能否被搜索函数正确处理以及本次搜索应读取哪些资料。下面就沿这次查询逐项看这些检查解决什么问题。1. 参数能解析还要检查它是否符合这项操作的约定先明确这个搜索工具接受什么query是用空格分隔的关键词published_from是通知发布日期的下限包含当天使用YYYY-MM-DD不筛选发布时间时填null。收到调用后程序至少要分清下面几种情况收到的内容为什么不能直接搜索程序可以怎样检查工具名为delete_notices本次执行入口只提供通知搜索只接受程序预先登记的工具名参数文字只有{query:读书会JSON 尚未完整无法确定参数对象完整解析失败就拒绝不补出缺失部分query为123或缺少published_from字段类型或字段集合不符合约定检查必填字段、额外字段及类型query全是空格或长达几千个字符类型虽然是字符串却不是本例允许的查询去除首尾空白后检查内容与长度published_from为2026-02-30写法像日期但日历上没有这一天按日期解析并检查规定的格式前面几项主要检查“交来的是什么”后面几项检查“这个值是否可用”。只要求日期是字符串拦不住不存在的日期只确认搜索词是字符串也拦不住空查询。长度限制同样需要依据具体实现。在这个小例子里我们把参数文字限制为 4096 个字符把去除首尾空白后的关键词限制为 100 个字符避免把过大的输入交给解析器或搜索函数。这是教学程序的取值不是所有搜索工具都应采用的标准。实际服务还要根据请求大小、搜索成本等条件确定限制。模型接口提供的结构约束可以减少格式错误程序侧仍应执行自己依赖的检查。尤其是当前权限、资料归属等条件不能由生成参数的结构约束决定。检查也不应顺带扩大工具能力。程序只登记了搜索就只调用对应的搜索函数不能把模型给出的名称当作代码执行也不能因为名称看起来像某个函数就在整个程序里寻找它并调用。2. 访问谁的资料要由程序在读取时决定现在给教学资料增加一位用户。这个练习不设置共享授权每位用户只能读取自己保存的通知。三份通知都含有“读书会”和“报名”但归属不同通知保存者发布日期主要内容D1当前用户 Alice2026-09-10报名原定于 9 月 20 日截止D2当前用户 Alice2026-09-16报名截止日期提前到 9 月 18 日D3另一位用户 Bob2026-09-16读书会志愿者报名联系名单Alice 搜索“读书会 报名”关键词能够匹配三份资料。但属于她的结果只能是 D1、D2。匹配关键词和允许读取是两个不同的条件。程序需要知道两个值当前登录用户是谁以及这个用户当前是否允许搜索。这些值应来自程序可信的登录会话和权限判断不是模型在参数里填写的身份。模型输出一个user_id只能说明它生成了这个字符串不能证明这次请求属于该用户。因此模型可填写的仍然只有query和published_from。执行时程序另外带入当前用户并在数据读取处限定范围否是否是接收工具名和参数名称与参数检查通过?拒绝调用不读取资料当前用户允许搜索?按程序确定的用户限定资料范围在该范围内搜索关键词与发布日期返回匹配通知这个顺序很重要。如果先把所有人的通知交给模型再要求它“只回答 Alice 的资料”其他人的内容已经进入了模型输入。即使最终答案没有显示 D3也不能说访问范围得到了控制。限定范围必须发生在资料交给模型之前查询数据库时应把用户或授权范围作为数据查询条件落实。公开工具实现中也能看到这种身份来源的划分。LangChain 官方工具文档的账户查询示例中应用传入UserContext(user_id...)账户查询函数从runtime.context.user_id取得用户编号再读取对应账户。文档还说明runtime不属于模型填写的工具参数。这里值得借鉴的是身份通过程序提供的上下文进入工具函数按这个身份取数据。这个公开示例没有展示完整的登录验证或权限系统把身份字段从模型参数中移走以后应用仍要保证传入的身份可信并落实实际访问规则。只读工具也需要这项控制。搜索不会修改通知但读到另一个人的资料本身就已经越过了允许访问的范围。3. 用一个能运行的小程序观察检查怎样阻止执行下面的练习只搜索三份虚构通知不联网、不调用模型。输入是已经从完整工具调用中提取出的工具名和参数文字便于单独观察执行检查。真实接入时接收层还应先确认响应和调用内容完整不能把未完成的参数送进执行入口。代码中的Context保存程序提供的用户与权限。为了能在本地直接运行示例手工固定这两个值接入真实应用时必须由服务端根据登录会话和执行时的权限产生不能照搬浏览器请求或模型输出中的同名字段。使用 Python 3.10 或更高版本无需安装额外依赖。将下面的完整程序命名为tool_guard.py运行python-X utf8-B tool_guard.py完整只读练习代码importjsonfromdataclassesimportdataclassfromdatetimeimportdatedataclass(frozenTrue)classContext:user_id:strcan_search:boolclassRejectedCall(ValueError):passNOTICES({id:D1,owner_id:u-alice,published_on:2026-09-10,content:本次读书会报名将于2026年9月20日截止。,},{id:D2,owner_id:u-alice,published_on:2026-09-16,content:本次读书会报名截止日期提前到2026年9月18日原通知中的截止日期作废。,},{id:D3,owner_id:u-bob,published_on:2026-09-16,content:九月读书会志愿者报名联系名单由另一位用户保存。,},)defunique_fields(pairs):result{}forkey,valueinpairs:ifkeyinresult:raiseRejectedCall(invalid_arguments)result[key]valuereturnresultdefreject_constant(value):raiseRejectedCall(invalid_arguments)defvalidate_arguments(text):ifnotisinstance(text,str)orlen(text)4096:raiseRejectedCall(invalid_arguments)try:argsjson.loads(text,object_pairs_hookunique_fields,parse_constantreject_constant,)except(ValueError,RecursionError)aserror:raiseRejectedCall(invalid_arguments)fromerrorifnotisinstance(args,dict)orset(args)!{query,published_from}:raiseRejectedCall(invalid_arguments)queryargs[query]ifnotisinstance(query,str)ornot1len(query.strip())100:raiseRejectedCall(invalid_arguments)sinceargs[published_from]ifsinceisnotNone:ifnotisinstance(since,str):raiseRejectedCall(invalid_arguments)try:parseddate.fromisoformat(since)exceptValueErroraserror:raiseRejectedCall(invalid_arguments)fromerrorifparsed.isoformat()!since:raiseRejectedCall(invalid_arguments)sinceparsedreturnquery.strip(),sincedefuser_notices(user_id):return[rowforrowinNOTICESifrow[owner_id]user_id]defsearch_notices(query,since,context):ifnotcontext.can_search:raiseRejectedCall(forbidden)matches[]forrowinuser_notices(context.user_id):ifsinceisnotNoneanddate.fromisoformat(row[published_on])since:continueifall(wordinrow[content]forwordinquery.split()):matches.append({key:row[key]forkeyin(id,published_on,content)})return{status:ok,matches:matches}defexecute_call(name,arguments_text,context):ifname!search_notices:raiseRejectedCall(unknown_tool)query,sincevalidate_arguments(arguments_text)returnsearch_notices(query,since,context)if__name____main__:contextContext(user_idu-alice,can_searchTrue)arguments{query:读书会 报名,published_from:null}try:resultexecute_call(search_notices,arguments,context)exceptRejectedCallaserror:result{status:rejected,reason:str(error)}print(json.dumps(result,ensure_asciiFalse,indent2))这里最值得跟着看的是三处execute_call只接受登记的工具名validate_arguments检查并解释参数search_notices在取得资料前检查权限再通过user_notices取得当前用户范围内的通知。两个 JSON 解析辅助函数用来拒绝重复字段以及NaN等非标准数值。Python 的 JSON 文档说明默认解码器会接受这些情况如果参数约定不允许就需要明确拒绝。日期使用标准库的 日期解析方法再确认格式与约定一致。直接运行默认输入实际返回statusok通知编号为 D1、D2没有 D3。再用人工准备的请求分别触发几种条件得到以下结果。这些输入用于验证程序的检查行为不代表模型在实测中生成过这些错误。请求变化实际处理结果是否进入资料读取当前用户 Alice关键词为“读书会 报名”不筛选发布时间成功返回 D1、D2是范围为 Alice 的通知将发布日期下限改为2026-09-15成功只返回 D2是范围仍为 Alice 的通知将query改为数字123拒绝invalid_arguments否将日期改为2026-02-30拒绝invalid_arguments否将工具名改为delete_notices拒绝unknown_tool否在参数里额外加入user_id:u-bob拒绝invalid_arguments否保留正常参数但程序提供的can_search为False拒绝forbidden否这份结果要核对的不只是返回文字。检查程序同时观察了资料读取函数有没有被调用上表中被拒绝的请求都没有进入该函数。把执行时的用户改为 Bob正常请求只返回 D3也说明范围随程序提供的身份变化没有固定成某一位用户。示例在内存中按保存者筛选资料便于直接看见规则。接入真实系统时需要在服务端的资料服务或数据库访问处落实同样的边界本地手工指定的身份与权限不能代替登录验证。4. 拒绝以后不能为了“让调用成功”悄悄改掉条件检查发现问题时处理方式应保留原任务的含义。去除关键词首尾空白是本例允许的整理把不存在的 2 月 30 日改成 2 月 28 日却已经改变了查询条件。同样缺少published_from不应被自动解释成“从今天开始”空字符串也不应悄悄变成null。前者可能漏掉需要核对的旧通知后者掩盖了调用方没有遵守约定的问题。程序没有足够依据时应拒绝这份参数让调用方纠正必要时向用户确认。权限拒绝则需要另一种处理。当前用户不允许搜索不能靠换一组关键词恢复资格也不能改成搜索其他用户。只能在程序确认权限已恢复之后重新判断新的调用能否执行。练习代码用RejectedCall表示这些拒绝运行入口把它显示为statusrejected。这与“查询成功但没有匹配通知”不同前者没有执行查询后者已经在允许范围内查过。记录和展示时保留这个区别才不会把权限失败误解成资料不存在。5. 检查通过为什么仍然可能查错问题最后看一个程序不能靠上述规则识别的错误。用户问报名截止日期是不是 2026 年 9 月 18 日假如模型把这个日期填成通知发布日期下限{query:读书会 报名,published_from:2026-09-18}字段齐全类型正确日期合法当前用户也允许搜索。这个请求会通过检查但在教学资料里实际返回空列表D1、D2 都是在 9 月 18 日之前发布的。错在两个日期的关系。用户给的是待核对的报名截止日期工具参数却被解释成“只查这天及之后发布的通知”。仅检查参数对象程序不知道这个日期是怎样从问题中取得的因此无法证明它符合用户原意。能够确定的业务条件应继续放进程序检查。例如用户在页面上明确选择了“从 9 月 15 日发布的通知开始查”程序可以直接带入这个值或者核对模型参数是否与选择一致。只有自然语言问题、没有独立确定条件时还需要通过清楚的工具说明、针对性任务和结果核对检查模型是否理解了日期的用途。不能看到参数校验通过就把“这次请求允许执行”扩大为“这次搜索一定能回答问题”。再试着判断两个变化搜索工具定义发给模型时用户有权限模型返回参数之前权限已经被撤销。仍然执行吗模型请求read_notice(D3)编号真实存在、格式正确但 D3 属于 Bob。Alice 可以读取吗第一种情况要以实际执行时的权限为准。第二种情况即使程序提供了按编号读取的工具也要在读取那份通知时检查归属或授权编号存在不等于当前用户可以访问。搜索入口有范围控制不能代替另一个读取入口的检查。回到开头程序拿到参数后需要确认操作名称、参数结构、可确定的取值条件、当前权限与实际访问范围。通过这些检查才允许在限定范围内执行执行是否成功、结果是否足以支持回答仍要依据真实返回继续判断。
阅读完成 · 觉得有帮助?