上下文工程实践 - 工具管理(上篇)

上下文工程实践 - 工具管理(上篇)

工具管理涉及的概念较多,所以我分成上下两篇来介绍:

上篇:主要讲 MCP 与 Tool 的定义和区别,以及工具的定义规范

下篇:继续展开,讨论工具的定位、错误处理方式和常见分类

1、什么是 MCP 和 Tool

  • MCP :是一份规范/协议,规定了"容器要怎么长、里面的功能要怎么描述、调用要怎么沟通"。就像 HTTP 规范本身,不是一个服务,而是大家都得遵守的语言。
  • MCP服务器 :是规范的一个实现,它作为一个容器,可以对外暴露很多能力(Tool / Resource / Event)。就像一个跑在端口上的 Web 服务器,遵循 HTTP 协议,里面可以有很多 API。
  • Tool :是容器里的功能单元,一般表现为一个"函数"或者"接口"。调用的时候就是传参执行,返回结果。

Tool 目前使用起来是比较简单的,在项目中按照规范写一个函数,将这个函数以 tool 参数的形式传递给大模型,大模型输出函数名之后,系统调用该函数

如果你需要搭建一套工具集的服务,想要提供给多个项目使用,提供给别人使用,提供给支持 MCP 的客户端使用,那么你就可以遵守 MCP 协议搭建 MCP 的服务

我觉得 MCP 和 Tool 本质是差不多的,就是给模型提供"外部能力"的作用,根据自己的情况和能力来选择使用是比较好的

2、工具定义规范

在定义工具的时候,最重要的就是工具的描述和工具调用需要的参数

  1. **工具的描述:**清晰的工具描述可以提高大模型工具调用的成功率
  2. **工具的参数:**描述参数工具的时候,要从参数名,参数类型,参数描述等尽可能多的方面讲这个参数表达的更清楚,这样在大模型输出工具参数的时候会准确和很多
  3. **工具的执行函数:**这个是一个函数,用于大模型输出合适的工具调用指令之后,系统执行该函数

2.1、工具(Tool)的类型定义:

  • description🌟:工具的描述
  • parameters🌟(ToolParameterDefinition):工具的参数定义
  • category:工具分类
  • version:工具版本
  • handler:工具的执行函数,这个不需要传递给大模型,而是作为系统内部执行的
  • agentAccessible:控制工具是否对代理可见
  • internal:标记为内部工具类
  • Purpose:描述了工具的核心目的和功能定位,它回答"这个工具为什么存在"以及"它解决啦什么问题",其是一个宏观的功能描述,例如:purpose: 'Perform semantic search over stored memory entries to retrieve relevant knowledge and reasoning traces that can inform current decision-making.'
  • UseCase:描述了工具的具体使用场景和适用条件,它回答了"在什么情况下应该使用这个工具"以及"如何正确的使用这个工具",其更多的关注应用场景和适用条件,例如:useCase: 'Use when you need to find previously stored knowledge, code patterns, or technical information that may be relevant to answering current questions or solving problems.'

descriptionparamenters**handler**是工具定义必须要的参数,其他的参数是根据情况选择的

handler 是工具的执行函数,这个不用作为工具定义信息传递给大模型

2.2、工具参数(ToolParameterDefinition)的类型定义:

  • type :定义参数数据类型,如:'string' | 'number' | 'boolean' | 'object' | 'array'
  • description:提供参数的人类可读描述,帮助 LLM 理解参数的用途
  • minimum | maximum:定义参数数值的最小值和最大值
  • minItems | maxItems:定义数组参数的最小和最大元素数量
  • minLength | maxLength:定义字符串参数的最小和最大长度
  • oneOf:定义参数必须匹配多个 Schema 中的一个
  • additionalProperties:禁止对象参数包含额外属性
  • items:定义数组元素的类型
  • enum:定义参数的可能取值
  • default:定义参数的默认值

typedescription 是必须的,这个是参数定义的基本元素,并且参数命名也可以成为参数的描述

下面是一个工具定义完整的案例:

typescript 复制代码
export const memoryOperationTool: InternalTool = {
	name: 'memory_operation',
	category: 'memory',
	internal: true,
	description:
		'Process extracted knowledge and determine memory operations (ADD, UPDATE, DELETE, NONE) using LLM-powered intelligent reasoning and similarity analysis with existing memories.',
	version: '2.0.0', // version
	parameters: {
		type: 'object',
		properties: {
			extractedFacts: {
				type: 'array',
				description:
					'Array of knowledge facts already extracted from interactions, containing technical details, code patterns, or implementation information.',
				items: {
					type: 'string',
				},
			},
			existingMemories: {
				type: 'array',
				description: 'Array of existing memory entries to compare against for similarity analysis.',
				items: {
					type: 'object',
					properties: {
						id: {
							type: 'string',
							description: 'Unique identifier of the existing memory',
						},
						text: {
							type: 'string',
							description: 'Content of the existing memory',
						},
						metadata: {
							type: 'object',
							description: 'Optional metadata for the memory',
						},
					},
					required: ['id', 'text'],
				},
			},
		},
		required: ['extractedFacts'],
	},
	handler: memoryOperationHandler
	},
};

//工具调用函数
const memoryOperationHandler = async (
	args: MemoryOperationArgs,
	context?: InternalToolContext
): Promise<MemoryOperationResult> => {
	// TODO: Implement intelligent reasoning logic to determine memory operations
	// Example placeholder return structure
	return {
		operations: [], // List of { operation: 'ADD' | 'UPDATE' | 'DELETE' | 'NONE', fact, memoryId? }
	};
};

2.3、工具执行函数定义

上面工具定义和工具参数定义这些都在作为 tool 参数传递给 LLM ,本质上就是 LLM 根据传递的工具描述和用户输入挑选出来一个合适的工具,但是大模型不负责执行工具函数,它只负责输出工具名称和工具参数

对于工具执行函数的定义,可以简单的根据工具参数去定义的,但是还是有一些常规定义方法,可以参考

工具的执行函数,会定义两个参数对象

  • arge:函数执行需要的参数对象
  • context |metadata:函数的元信息

一般来说,arge 是刚刚传入工具参数的类型,是工具执行函数的基本参数,而 context 或者 metadata 是属于辅助参数,大部分情况是用于监控和统计的,是可选的,没有这个也不影响函数的主功能执行

例如上面的定义案例中的memoryOperationHandler

typescript 复制代码
//主要参数-参数对象
export interface MemoryOperationArgs {
	extractedFacts: string[];
	existingMemories?: {
		id: string;
		text: string;
		metadata?: Record<string, any>;
	}[];
}

//辅助参数-元信息
export interface InternalToolContext {
    toolName: string;
    startTime: number;
    sessionId: string | undefined;
    userId?: string;
    metadata: Record<string, any> | undefined;
    
    // 服务依赖
    services?: {
        embeddingManager?: EmbeddingManager;
        vectorStoreManager?: VectorStoreManager;
        llmService?: ILLMService;
        knowledgeGraphManager?: KnowledgeGraphManager;
    };
}

//工具执行函数
const memoryOperationHandler = async (
	args: MemoryOperationArgs,
	context?: InternalToolContext
): Promise<MemoryOperationResult> => {
	// TODO: Implement intelligent reasoning logic to determine memory operations
	// Example placeholder return structure
	return {
		operations: [], // List of { operation: 'ADD' | 'UPDATE' | 'DELETE' | 'NONE', fact, memoryId? }
	};
};

最后

本文节选自我正在整理的 「上下文工程实践」 项目,该项目已完整发布在 GitHub 上。

如果你希望阅读更多相关的章节与案例,可以前往项目仓库查看:

👉 github.com/WakeUp-Jin/...

相关推荐
_UMR_11 小时前
springboot集成Jasypt实现配置文件启动时自动解密-ENC
java·spring boot·后端
程序员小假11 小时前
我们来说说 Cookie、Session、Token、JWT
java·后端
短剑重铸之日11 小时前
《SpringBoot4.0初识》第一篇:前瞻与思想
java·开发语言·后端·spring·springboot4.0
it_czz11 小时前
LangSmith vs LangFlow vs LangGraph Studio 可视化配置方案对比
后端
蓝色王者11 小时前
springboot 2.6.13 整合flowable6.8.1
java·spring boot·后端
华仔啊12 小时前
JavaScript 如何准确判断数据类型?5 种方法深度对比
前端·javascript
花哥码天下12 小时前
apifox登录后设置token到环境变量
java·后端
程序员小寒12 小时前
从一道前端面试题,谈 JS 对象存储特点和运算符执行顺序
开发语言·前端·javascript·面试
爱健身的小刘同学13 小时前
Vue 3 + Leaflet 地图可视化
前端·javascript·vue.js