AI Agent实战总结 - 维护测试数据
项目背景我们需要维护一批测试数据。该测试数据包括多个时间序列的值表示从小时x到小时y的数据。因为时间范围比较大由人工对数据进行比对和编辑比较麻烦。同时我们希望非技术人员可以对该数据进行核对和直接修改。我们现在希望达到的目标是在本地setup一个环境使得技术人员和非技术人员都可以对该测试数据进行维护。解决方案我们在本地setup Ollama和运行开源模型 - qianwen3:14b。使用AI model的目的是可以处理自然语言这样使得非技术人员可以同样方便的对数据进行维护。另外我们setup一个jupyter notebook用来运行ai agent。使用jupyter notebook的主要原因是借助于matplot lib我们可以方便的将测试数据的图表化方便维护人员进行查看。同时我们需要定义一系列的tools以供ai agent调用来对数据进行真正的处理。整体系统的架构图如下所示实现细节搭建开发环境这里的实现是基于mac操作系统。对于其他的操作系统区别不大。编程语言选择python。首先安装Ollama。过程省略请参考Ollama网站。然后下载并启动qianwen3:14bollama run qianwen3:14b然后我们setup python运行环境。我们创建一个本项目的folder然后启动vscode。在vscode的terminal里用下面的命令建立virtual environment并启动python -m venv venv source ./venv/bin/activate下面在folder的根目录下创建文件requirements.txt在文件里定义python环境需要包含的package如下pandas matplotlib numpy pylzma ipykernel jupyter langchain langchain-core langchain-community langchain-ollama然后运行下面的命令安装这些被依赖的packagepip install -r requirements.txt最后一步建立一个空的jupyter notebook。在根目录下建立一个文件以.ipynb结尾然后双击该文件在编辑框中打开该jupyter notebook。编码数据schema我们使用LangChain作为ai agent的architecture。首先我们定义一下我们数据的schema。这里仅举一个例子事实上python处理任何schema都是很容易的。{level: 5,hierarchy: {level_5: System},time: 1735526775,values: [{status: NORMAL,hour: -8,value: 25},{status: MODERATE,hour: -7,value: 60}, ..., {status: HIGH,hour: 120,value: 80}]} {level: 4,hierarchy: {level_5: System, level_4: Department},time: 1735526775,values: [{status: NORMAL,hour: -8,value: 25},{status: MODERATE,hour: -7,value: 60}, ..., {status: HIGH,hour: 120,value: 80}]} ...我们的数据文件里分为多行每行是一条记录。一条记录内部有一个成为hierarchy的部分里面包含从”level_0“到”level_5“的信息。level_5为根。每一层仅包含从当前层到根的信息。比如第三层的hierarchy仅包含从第三层到第五层的信息而不包含第零层到第二层的信息。另一个重要的部分是values。values是一只列表包含多个value的记录。我们目前仅关心value的值可以忽略掉其它的attributes。普通方法定义我们首先定义两个方法它们实现了记录的查找和values的值提取功能。这两个方法作为ai agent的tools的主要功能的实现。import json # Search lines in file. def search_in_file( file_path: str, keywords: list ) - list: print(fsearch file is called, {file_path}, {keywords}) matches [] with open(file_path) as f: for i, line in enumerate(f): if hierarchy not in line: continue hierarchy json.loads(line)[hierarchy] if len(hierarchy) ! len(keywords): continue matched True for keyword in keywords: if not keyword in hierarchy.values(): matched False break if matched: matches.append(f{line}) return matches # Extract pressure values from pressure. def extract_pressure_values(pressures: list) - list: print(extract_pressure_values is called) values [] for pressure in pressures: structured_pressure json.loads(pressure) values structured_pressure[values] value [value_record[value] for value_record in values] values.append(value) return valuesTools下面基于这两个方法定义tools。tools可以理解为 Agent 的“外部能力扩展”。这里我们不对LangChain做具体介绍。关于LangChain的用法可以在其官网查看代码示例和文档。import re import matplotlib.pyplot as plt import matplotlib.colors as mcolors from langchain.tools import tool def get_color(v): if v 49: return green elif v 65: return yellow elif v 74: return orange else: return red tool def visualize_pressures(file_path: str, hierarchy: list, title: str) - None: Visualize pressures. print(fvisualize data is called, {file_path}, {hierarchy}, {title}) if len(hierarchy) 1: hierarchy_items [hierarchy_item.strip() for hierarchy_item in re.split(r[^A-Za-z -_], hierarchy[0]) if hierarchy_item.strip()] else: hierarchy_items hierarchy pressures search_in_file(file_path, hierarchy_items) for values in extract_pressure_values(pressures): hours range(-8, -8 len(values)) colors [get_color(v) for v in values] plt.figure(figsize(15, 5)) plt.bar(hours, values, colorcolors) plt.xlabel(Hour) plt.ylabel(Value) plt.title(title) plt.show()tools的方法需要三个参数file_path: 数据文件的路径hierarchy: 对应于数据记录里的hierarchy部分信息。我们用hierarchy来定位需要维护的数据记录title我们的tools将使用matplotlib绘图这个参数是图的title。代码的逻辑比较简单。我们利用刚才的两个方法找到需要处理的数据记录然后将value的值提取为一个list然后用matplotlib绘制柱状图。有两个地方的逻辑需要稍微介绍一下1. 关于下面的逻辑if len(hierarchy) 1: hierarchy_items [hierarchy_item.strip() for hierarchy_item in re.split(r[^A-Za-z -_], hierarchy[0]) if hierarchy_item.strip()]我们传入的hierarchy期望被解析为一个list也就是[system, department, ...]但是ai agent有时将一个hierarchy解析为一个字符串例如system,department。我们判断如果只有一个元素则尝试将这个元素分解为多个元素。2. 关于下面的逻辑def get_color(v): if v 49: return green elif v 65: return yellow elif v 74: return orange else: return red我们针对不同的value值绘画成不同的颜色以利于对值的合法性进行查看。Agent和model首先我们如下定义model并将我们定义好的tools捆绑到model上from langchain_ollama import ChatOllama from langchain.agents import create_agent model ChatOllama(modelqwen3:14b) llm_with_tools model.bind_tools([visualize_pressures])在这里我们没有定义真正意义上的ai agent而是直接使用model和model捆绑的tools。其原因如下。正常使用ai agent处理prompt时处理的流程为user prompt - llm model - tools - llm model - user。也就是说在tools返回的值基础上ai agent会在返回给user之前使用ai model对返回的结果做一次推理。因为我们使用的是本地开源ai model功能相对较弱对应输出的内容和格式控制不是很好。为了加强输出格式和内容的稳定性我们将只使用ai model对调用的tools进行推理然后直接调用tools并返回结果省略掉返回给用户之前的推理。调用tools的逻辑如下tool_map { visualize_pressures: visualize_pressures, } def execute_query(user_query: str, llm_with_toolsllm_with_tools) - None: response llm_with_tools.invoke(user_query) for tc in response.tool_calls: tool tool_map[tc[name]] tool.invoke(tc[args])方法execute_query()接收用户的prompt调用ai model并查看tool_calls。这一部分是ai model对tools调用的推理结果。我们根据这一步的结果直接调用tools并且传入ai model生成的调用参数。Demo和结尾我们将输入自然语言作为prompt然后ai model会对我们的prompt进行推理并生成适当的输入参数。下面是我们的prompt定义user_query visualize pressure of System, CH-MT2500SD in file ./data/pressure.txt. execute_query(user_query)我们会看到tools被正常解析和调用visualize data is called, ./data/pressure.txt, [System, CH-MT2500SD], CH-MT2500SD search file is called, ./data/pressure.txt, [System, CH-MT2500SD] extract_pressure_values is called正确的图表将显示在我们的jupyter notebook里在本项目中我们尝试了使用Ollama的本地ai model解决由自然语言调用代码来解决真实技术问题的案例。我们也总结了一些本地ai model的不足和应对方法。接下来我们可以进一步丰富我们的tools和ai agent的功能。同时我们可以尝试使用LangGraph或者其他ai agent的framework解决我们现在实现方案的不足。