JIT-Agent:动态生成智能体框架的设计与实现

发布时间:2026/8/30 5:50:49
JIT-Agent:动态生成智能体框架的设计与实现 在搭建智能体Agent应用时最让人头疼的往往不是大模型本身而是 Agent 的“骨架”怎么搭工具列表写死、任务流程写死、上下文越堆越长……一旦业务需求变化静态配置的 Agent 就要改代码、发版本链路还特别容易断。JIT-Agent 正是为了解决这类问题出现的一种动态生成智能体框架的实现思路。这里所说的“模型”并不是指某一个预训练大模型而是一种框架层面的设计模型JITJust-In-Time即时生成。它借鉴了 JIT 编译“按需生成、运行时执行”的思想把 Agent 中的工具选择、提示词组装、执行计划从启动阶段推迟到每一次任务请求发生时动态完成。本文会从核心概念讲起再手把手实现一个轻量 JIT-Agent 框架最后给出常见问题和工程建议。1. 背景与核心概念1.1 什么是 JIT-AgentJIT-Agent 可以理解为一种“动态生成智能体框架”的设计模型。通常一个 Agent 由三部分组成大模型LLM、工具集Tools、执行策略Execution Strategy。传统做法是在系统启动时把全部工具和固定提示词注册进去之后所有请求都走同一套逻辑。这种模式实现简单但灵活性不足。JIT-Agent 改变了生成时机当用户请求到达时框架先分析当前任务确定“这个任务需要用到哪些工具”再去注册表中动态加载对应的工具元数据和执行函数同时生成针对性的系统提示词最后执行任务并返回结果。整个过程像 JIT 编译一样不是提前把一切准备好而是“用到什么才生成什么”。这样做的好处很明显减少上下文中的工具描述节省 Token模型不容易被无关工具干扰新增工具时无需修改主流程只要在注册表中登记即可可以根据不同用户、不同场景生成不同形态的 Agent。1.2 JIT-Agent 解决什么问题静态 Agent 在实际项目中会遇到几个突出问题。第一工具全量注册导致上下文膨胀。假设一个 Agent 注册了 30 个工具每次调用大模型时都要把 30 个工具的描述传给模型即使本次任务只需要其中 2 个。随着工具数量增长Token 开销会越来越大模型出错的概率也会上升。第二任务流程固定难以应对开放性问题。很多 Agent 是“定义好 Function、定义好 Prompt、执行固定流程”对简单任务勉强可用但遇到无法预料的用户输入就会显得僵化。第三扩展成本高。每次新增工具都要修改 Agent 的初始化代码甚至要调整提示词模板维护成本非常高。JIT-Agent 的核心思路是“以任务为中心”先把所有工具注册到一个中心注册表每次请求到来时由规划器决定加载哪些工具再动态生成执行计划。这样一来Agent 的结构与具体业务解耦新增工具只需要写一个函数并注册无需改动主框架。当然动态生成也不是没有代价。运行时分析任务、动态加载工具都会带来额外延迟而且规划结果可能不稳定。所以 JIT-Agent 更适合任务类型丰富、工具数量较多、需要快速扩展的场景。1.3 典型应用场景JIT-Agent 的适用面很广常见的有下面几类。智能客服系统用户问“我的订单什么时候到”后台动态加载订单查询工具用户问“退款流程是什么”后台加载退款相关文档工具。数据分析平台用户输入“帮我画一个 7 月销售额折线图”Agent 动态加载数据查询工具、图表生成工具不需要把所有 BI 能力全部暴露给模型。自动化运维助手根据告警关键字动态选择日志查询、服务重启、健康检查等工具避免把所有危险操作都暴露在同一个上下文中。个人助理根据用户当前需求动态决定调用天气、日历、导航还是音乐播放工具。这些场景有一个共同特征工具数量多、任务类型分散、单次请求实际用到的工具很少。如果全部静态加载效率和安全都不理想。2. 环境准备与版本说明2.1 运行环境本文的示例代码使用 Python 编写核心部分只依赖 Python 标准库不需要安装额外第三方包方便读者直接复制运行。操作系统Windows / macOS / Linux 均可Python 版本建议 3.9 及以上IDEVS Code、PyCharm 均可能运行 Python 脚本即可可选依赖如果后续要接入真实大模型需要根据你使用的模型服务安装对应 SDK版本以官方文档为准。示例项目结构如下jit_agent/ ├── agent/ │ ├── __init__.py │ ├── registry.py │ ├── planner.py │ ├── executor.py │ └── runtime.py ├── tools/ │ ├── __init__.py │ ├── weather.py │ └── calculator.py └── main.pyagent包负责框架核心逻辑tools包存放具体业务工具main.py是入口。这样分层的好处是框架与业务工具分离新增工具不需要改动框架代码。2.2 版本说明关于 Python 版本建议使用 3.9 以上因为代码中用到了类型注解等特性更低版本可能需要调整。如果你要在生产环境中接入真实大模型版本需要根据你实际使用的模型服务调整本文示例重点演示框架实现思路。3. JIT 动态生成的核心原理拆解3.1 从“静态 Agent”到“动态生成”先看一个静态 Agent 的典型实现。# 静态注册启动时写死 def get_weather(city: str): return f{city} 天气晴 def calculate(expression: str): return eval(expression) TOOLS { get_weather: get_weather, calculate: calculate, }这种方式在工具数量少时没有问题但如果工具越来越多问题就来了每个请求都要把所有工具的描述拼进 Prompt工具间如果有依赖关系静态加载容易遗漏或冲突新增工具需要修改TOOLS字典和 Prompt 模板。JIT-Agent 的做法是引入“注册表 规划器”两层结构。注册表保存所有工具的元数据和执行函数规划器在运行时根据用户输入决定加载哪些工具。核心思想是让“工具选择”变成动态过程。3.2 工具注册表动态生成的基础注册表是 JIT-Agent 的地基。它维护一个全局字典每个工具条目至少包含四个信息工具名称、工具描述、参数 Schema、执行函数。名称用于唯一标识描述用于给规划器做选择参数 Schema 用于校验和生成调用参数执行函数是真正运行的业务逻辑。Python 中可以用装饰器实现一个极简注册表。# agent/registry.py TOOL_REGISTRY {} def register_tool(name, description, parameters_schemaNone): def decorator(func): TOOL_REGISTRY[name] { name: name, description: description, parameters: parameters_schema or {}, func: func, } return func return decorator def get_tool(name): return TOOL_REGISTRY.get(name) def list_tools(): return [ { name: tool[name], description: tool[description], parameters: tool[parameters], } for tool in TOOL_REGISTRY.values() ]这里的关键点是list_tools只返回元数据不返回执行函数。这样在把工具列表发给大模型时不会暴露内部实现。执行函数只在真正执行时才通过get_tool获取这是动态生成的关键一步。3.3 任务规划动态选择工具的入口注册表准备好了剩下的问题是怎么知道当前任务需要哪些工具这就是“规划器”的职责。规划器的输入是用户请求文本输出是一个或多个“工具调用计划”每个计划包含工具名和参数。最简单的方式是关键词匹配适合演示更通用的方式是调用大模型的 function calling 能力让模型自己从工具列表中选择合适的工具。不管是哪种方式规划器的核心逻辑都一致先用当前注册表中的工具元数据生成候选列表再根据用户输入选择工具并提取参数。这就是“动态生成”的核心体现工具列表不是固定的而是随着请求变化。4. 完整实战手写一个轻量 JIT-Agent 框架4.1 定义业务工具先创建tools/weather.py定义一个查询天气的工具。# tools/weather.py from agent.registry import register_tool register_tool( nameget_weather, description查询指定城市的天气情况, parameters_schema{ type: object, properties: { city: {type: string, description: 城市名称例如北京} }, required: [city], }, ) def get_weather(city: str) - str: # 真实项目中可替换为天气 API 请求 return f{city} 今天晴气温 25 摄氏度空气质量良。再创建tools/calculator.py定义一个计算器工具。# tools/calculator.py from agent.registry import register_tool register_tool( namecalculate, description计算简单的四则运算表达式, parameters_schema{ type: object, properties: { expression: {type: string, description: 数学表达式例如 35} }, required: [expression], }, ) def calculate(expression: str) - str: # 演示代码直接使用 eval生产环境请使用安全解析库 result eval(expression) return f{expression} {result}到这里工具定义已经完成。注意两个文件都通过register_tool装饰器把函数注册到全局注册表但主流程代码并没有直接引用它们。这意味着将来新增工具时只需要新增一个文件并导入一次即可。4.2 实现工具注册表之前已经给出registry.py的代码这里再补充一个细节工具注册顺序会影响规划器看到的候选列表。实际项目中建议在入口文件统一导入所有工具模块保证注册完成后再启动 Agent。# agent/__init__.py这个文件保持为空用于标识agent是一个 Python 包。4.3 实现任务规划器接下来实现agent/planner.py。为了做到“零依赖即可运行”演示版本用关键词匹配生成计划。# agent/planner.py class Planner: def __init__(self, tools): self.tools tools def plan(self, user_input: str) - list: 根据用户输入生成执行计划。 演示版本使用关键词匹配生产环境可替换为 LLM function calling。 plans [] if 天气 in user_input: city 北京 for candidate in [北京, 上海, 广州, 深圳]: if candidate in user_input: city candidate break plans.append({tool: get_weather, arguments: {city: city}}) if 计算 in user_input or 算一下 in user_input: expression user_input.replace(计算, ).replace(算一下, ).strip() plans.append({tool: calculate, arguments: {expression: expression}}) return plansPlanner接收tools参数但在关键词匹配版本中没有直接使用。保留这个参数是为了后续替换成 LLM 规划器时可以基于工具列表生成 function calling 请求。如果用户输入同时包含“天气”和“计算”比如“先看看北京天气再计算 35”规划器会生成两个计划按顺序执行这已经具备多步任务的雏形。4.4 实现执行引擎执行引擎负责真正调用工具函数。它从计划中取出工具名和参数从注册表中获取执行函数然后执行并返回结果。# agent/executor.py from agent.registry import get_tool class Executor: def execute(self, plan: dict) - str: tool_name plan[tool] tool get_tool(tool_name) if not tool: return f错误工具 {tool_name} 未注册 try: result tool[func](**plan.get(arguments, {})) return result except Exception as e: return f工具执行失败{e}这里做了两层保护第一层判断工具是否存在第二层捕获执行异常。实际项目中还应该增加参数校验确保调用参数符合工具的parameters_schema避免恶意输入或异常参数传入工具函数。4.5 实现运行时装配运行时是整个 JIT-Agent 的入口它把注册表、规划器、执行器组装在一起对外提供run方法。# agent/runtime.py from agent.registry import list_tools from agent.planner import Planner from agent.executor import Executor class JITAgent: def __init__(self): self.tools list_tools() self.planner Planner(self.tools) self.executor Executor() def run(self, user_input: str): print(f用户输入{user_input}) print(当前可用工具) for tool in self.tools: print(f - {tool[name]}: {tool[description]}) plans self.planner.plan(user_input) if not plans: return 没有找到合适的工具来处理该请求 outputs [] for plan in plans: print(f动态生成计划{plan[tool]} {plan.get(arguments, {})}) result self.executor.execute(plan) outputs.append(result) return \n.join(outputs)这个类体现了一个核心思想JITAgent在初始化时只获取工具元数据不直接绑定任何具体业务函数。每次运行请求时规划器和执行器动态配合完成“选工具、调工具”的过程。4.6 编写入口并运行验证最后创建main.py统一导入工具模块然后启动 Agent。# main.py from agent.runtime import JITAgent import tools.weather # noqa: F401 确保工具模块被导入并注册 import tools.calculator # noqa: F401 def main(): agent JITAgent() print(agent.run(北京天气怎么样)) print(---) print(agent.run(计算 35)) if __name__ __main__: main()在项目根目录运行python main.py预期输出类似用户输入北京天气怎么样 当前可用工具 - get_weather: 查询指定城市的天气情况 - calculate: 计算简单的四则运算表达式 动态生成计划get_weather {city: 北京} 北京 今天晴气温 25 摄氏度空气质量良。 --- 用户输入计算 35 当前可用工具 - get_weather: 查询指定城市的天气情况 - calculate: 计算简单的四则运算表达式 动态生成计划calculate {expression: 35} 35 8到这里一个最简 JIT-Agent 已经可以运行。接下来可以思考如何把关键词规划器替换成真正的大模型规划器。4.7 接入大模型 function calling 的改造思路真实生产环境中规划器应该让大模型来承担。以常见的 function calling 接口为例改造思路如下。# agent/llm_planner.py # 示例思路需按实际模型服务调整 def plan_with_llm(user_input: str, tools: list) - list: messages [{role: user, content: user_input}] functions [ { type: function, function: { name: tool[name], description: tool[description], parameters: tool[parameters], }, } for tool in tools ] # 调用你使用的模型服务这里以伪代码示意 # response chat_completion( # modelyour-model, # messagesmessages, # toolsfunctions, # ) # # 解析响应中的 tool_calls转换为 plan 列表 # plans [ # { # tool: call.function.name, # arguments: json.loads(call.function.arguments), # } # for call in response.choices[0].message.tool_calls # ] return []核心点是把list_tools()返回的元数据直接作为functions传给模型模型返回工具调用计划后执行引擎不用改动即可执行。这就是 JIT-Agent 最大的优势工具不断新增Agent 框架代码不变模型每次都能看到最新的工具列表。5. 常见问题与排查思路下面列出 JIT-Agent 在实践中最常见的几类问题。问题现象常见原因解决思路工具未注册执行时报错工具模块没有被导入在入口统一导入所有工具模块大模型选错工具工具描述含糊、相似工具太多优化描述增加示例减少相似工具参数解析报错function calling 返回的 JSON 格式不稳定增加 JSON 解析兜底做参数 Schema 校验上下文超长动态生成后仍然传入过多工具限制候选工具数量分批选择并发下状态混乱工具函数修改了全局状态工具函数尽量无状态避免共享可变对象动态加载延迟较高每次请求都重新分析任务增加缓存、预加载热工具下面逐个展开说明。工具未注册。这是使用注册表模式最容易踩的坑。register_tool装饰器在模块导入时执行如果某个工具模块没有被 import它的注册代码就不会运行。解决方法是把全部工具模块在main.py中统一导入或使用importlib按目录自动加载。大模型选错工具。工具列表是动态生成的但如果工具描述写得含糊模型仍然可能选错。比如get_weather和get_temperature功能相似模型很难区分。建议在描述里写清楚边界必要时在参数 Schema 的description中补充示例。参数格式不稳。不同模型对 function calling 的响应格式有差异有的模型返回的是 JSON 字符串有的返回结构化对象。建议在规划器中增加统一解析层解析失败时给模型一个“重新生成”的机会而不是直接报错。上下文超长。JIT-Agent 已经能减少无关工具但工具数量特别多时即使每个工具只描述一两行全部传给模型也会撑爆上下文。可以按工具分组或先用模型做一次粗筛再加载候选工具的详细描述。并发状态混乱。如果工具函数操作了全局变量多线程场景下容易出现互相覆盖。JIT-Agent 中工具函数应该尽量保持无状态所有输入通过参数传入输出通过返回值带出。动态加载延迟。每次请求都动态分析确实会带来额外耗时。在延迟敏感的场景中可以基于历史请求做热工具缓存把高频工具提前加载到内存。6. 最佳实践与工程建议6.1 工具注册表的命名与版本管理工具名称是全局唯一标识建议用“动词 名词”的格式例如query_order、create_ticket、send_email。避免使用模糊的do_task、run之类名称。工具数量增多后还要考虑版本管理。可以在注册表条目中增加version字段重要工具升级时保留旧版本方便灰度切换。注册表本身可以增加enabled开关让运营或开发临时下线有问题的工具而不用重新发布代码。6.2 配置与代码分离JIT-Agent 的动态特性容易让人把业务逻辑写进框架代码里这是需要避免的。工具列表、模型名称、超时时间、缓存策略等都应该放在配置文件中通过配置中心或环境变量管理。这样工具上线、参数调整不需要改代码。6.3 安全边界与权限控制动态生成框架最大的风险是“工具暴露面变大”。如果所有工具都注册到一个全局注册表一旦规划器被诱导就可能调用危险工具。因此需要做到以下几点工具函数内部做输入校验不轻信规划器传来的参数高危操作需要二次确认比如删除资源、发起支付不要在生产环境使用eval或exec计算器示例只是为了演示原理对大模型的工具选择结果做白名单校验只允许调用当前任务允许范围内的工具对用户输入做提示注入防护避免用户通过自然语言诱导模型调用未授权工具。6.4 日志与可观测性动态生成意味着每次请求的行为都可能不同必须把规划结果记录下来。建议至少记录以下内容用户原始输入本次请求加载了哪些工具规划器生成的工具调用计划每个工具的执行结果和执行耗时如果规划器调用大模型记录模型返回的原始响应。有了这些日志才能快速定位“模型选错工具”“参数错误”“工具执行异常”等问题。6.5 性能优化动态生成虽然灵活但性能也需要关注。常见优化手段包括工具元数据缓存避免每次请求都重新遍历注册表热工具预加载把高频工具的执行函数提前放入内存规划结果缓存对于相同或相似请求直接复用之前的工具调用计划并发控制避免大量请求同时触发大模型规划导致上游服务超时。6.6 生产环境注意事项上线前建议先做一次“全量工具扫描”确保所有工具模块都能正常导入、注册、执行。还要准备一个“最小工具集”兜底当规划器没有返回任何计划或返回的工具全部执行失败时Agent 应该能给用户一个明确回复而不是静默失败。如果使用真实大模型规划器要关注模型服务的限流和成本。可以给每次任务的规划调用设置超时时间并在超时后降级到关键词规划器保证核心链路可用。7. 总结与学习路线这篇文章主要围绕 JIT-Agent 展开核心内容可以总结为三点第一JIT-Agent 是一种动态生成智能体框架的设计模型核心是把工具选择和流程组装推迟到运行时第二实现一个轻量 JIT-Agent 的关键是注册表加规划器注册表负责管理工具元数据规划器负责按任务选择工具第三动态生成框架在灵活性上优于静态注册但同时要关注安全、日志、性能和可观测性。如果你对智能体框架感兴趣下一步可以继续学习三块内容一是 ReAct 模式搞清楚 Agent 如何在大模型推理和工具调用之间循环二是多 Agent 协作了解多个 JIT-Agent 如何分工配合三是工具自治让 Agent 在运行时自动生成工具描述甚至动态生成新的工具函数。无论走哪个方向都可以先把本文这套最小框架跑通再逐步往里面加入真实模型、持久化存储和更完善的工具管理能力。实际项目中优先关注的风险点有两个一个是工具暴露面变大带来的安全风险另一个是动态生成导致的可观测性变差。只要把注册表权限控制和日志链路做好JIT-Agent 就能在日常业务中发挥很大的灵活性。如果你正在设计自己的 Agent 框架不妨从一个小场景开始把一两个工具接入动态注册表跑通后再逐步扩展。如果这篇文章对你有帮助可以收藏备用也欢迎在评论区交流你在动态生成 Agent 时踩过的坑。

相关新闻