Arbiter——静态分析Agent的实现

📅 2026/6/26 2:20:18
Arbiter——静态分析Agent的实现
项目开发日记静态分析Agent的实现基于tree-sitter构建代码智能分析工具在代码审查流程中需要自动分析代码质量、发现潜在问题。我开发了这个静态分析Agent它能解析代码AST提取函数签名、调用图、复杂度指标并输出结构化的issues列表供下游消费。为什么是tree-sitter在对比了正则表达式和Python AST后我选择了tree-sitter原因是多语言支持不只限于Python还能支持JavaScript、Go等多种语言错误容忍即使代码有语法错误也能生成部分AST精确定位能精确获取每个节点的行列位置增量解析支持增量更新适合大型项目我甚至考虑过LLM这个能理解上下文提出建议但考虑成本延迟结果不可靠以及我这个项目的目的架构设计核心模块StaticAnalyzerAgent (主控制器) ↓ TreeSitterParser (解析封装) ↓ 分析引擎 (AST分析 正则降级) ↓ 结构化输出数据模型我定义了以下几个核心数据结构Issue发现的问题severity: error/warning/infoline: 行号description: 问题描述suggestion: 修复建议FunctionInfo函数信息name: 函数名start_line/end_line: 起止行号parameters: 参数列表complexity: 圈复杂度is_recursive: 是否递归calls: 调用的函数列表AnalysisResult分析结果success: 是否成功failure_category: 失败类型issues: 问题列表functions: 函数列表call_graph: 调用图metrics: 代码指标degraded: 是否降级核心功能实现1. 代码解析我封装了TreeSitterParser类来处理解析逻辑classTreeSitterParser:def__init__(self):self.python_languageLanguage(tspython.language())self.parserParser(self.python_language)asyncdefparse(self,code:str,language:str):iflanguage!python:raiseValueError(fUnsupported language:{language})treeawaitasyncio.to_thread(self.parser.parse,code.encode(utf-8),)returntree这里使用异步执行避免阻塞事件循环同时支持超时控制。2. 函数提取遍历AST提取所有顶层函数定义def_extract_functions(self,root_node,code):functions[]deftraverse(node):ifnode.typefunction_definition:func_infoself._parse_function_node(node,code)iffunc_info:functions.append(func_info)return# 不进入函数体内部只提取顶层ifnode.typeclass_definition:return# 类方法由_extract_classes处理forchildinnode.children:traverse(child)traverse(root_node)returnfunctions函数信息解析包括从name字段获取函数名从parameters子节点提取参数列表计算圈复杂度检测是否递归调用提取函数内部的调用关系3. 类信息提取类似地提取类定义及其方法def_extract_classes(self,root_node):classes[]deftraverse(node):ifnode.typeclass_definition:class_infoself._parse_class_node(node)ifclass_info:classes.append(class_info)forchildinnode.children:traverse(child)traverse(root_node)returnclasses4. 圈复杂度计算遍历函数AST统计控制流语句数量def_calculate_complexity(self,node):complexity1defcount_branches(n):nonlocalcomplexityifn.typein(if_statement,for_statement,while_statement,except_clause):complexity1forchildinn.children:count_branches(child)count_branches(node)returncomplexity5. 调用图构建从函数信息中提取调用关系def_build_call_graph(self,functions):return{func.name:func.callsforfuncinfunctionsiffunc.calls}6. 代码指标计算统计代码行数、注释行数和空行def_calculate_metrics(self,code):linescode.split(\n)total_lineslen(lines)code_linescomment_linesblank_lines0forlineinlines:strippedline.strip()ifnotstripped:blank_lines1elifstripped.startswith(#):comment_lines1else:code_lines1returnCodeMetrics(total_linestotal_lines,code_linescode_lines,comment_linescomment_lines,blank_linesblank_lines)7. 问题生成规则基于AST分析结果生成issuesdef_generate_issues(self,functions,code):issues[]forfuncinfunctions:# 高复杂度检查iffunc.complexity10:issues.append(Issue(severitywarning,linefunc.start_line,descriptionf函数{func.name}圈复杂度为{func.complexity}建议拆分,suggestion将复杂逻辑拆分为多个职责单一的子函数))# 参数过多检查iflen(func.parameters)5:issues.append(Issue(severitywarning,linefunc.start_line,descriptionf函数有{len(func.parameters)}个参数建议不超过5个,suggestion考虑使用dataclass或dict封装参数))# 函数过长检查func_lengthfunc.end_line-func.start_lineiffunc_length50:issues.append(Issue(severityinfo,linefunc.start_line,descriptionf函数有{func_length}行建议不超过50行,suggestion提取子函数以降低函数长度))returnissues8. 正则降级策略当tree-sitter解析失败时自动降级为正则模式匹配_REGEX_PATTERNS[(re.compile(reval\s*\(,re.MULTILINE),warning,使用eval()存在代码注入风险),(re.compile(rexcept\s*:,re.MULTILINE),warning,裸except会捕获所有异常),# ... 更多模式]def_regex_fallback(self,code):issues[]forpattern,severity,descriptionin_REGEX_PATTERNS:formatchinpattern.finditer(code):line_numcode[:match.start()].count(\n)1issues.append(Issue(severityseverity,lineline_num,descriptiondescription,suggestiondescription))returnAnalysisResult(successTrue,failure_categoryFailureCategory.PARSE_ERROR,error_messagetree-sitter解析失败降级为正则匹配,issuesissues,metricsself._calculate_metrics(code),degradedTrue)输出格式Agent输出JSON格式的结构化结果{issues:[{severity:warning,line:42,description:函数process_data圈复杂度为15建议拆分,suggestion:将复杂逻辑拆分为多个职责单一的子函数}],static_findings:found,degraded:false,metrics:{total_lines:200,code_lines:150,comment_lines:30,blank_lines:20}}当没有发现问题时标记static_findings: none{issues:[],static_findings:none,degraded:false,metrics:{...}}故障处理1. 解析失败降级当tree-sitter解析失败时自动切换为正则模式匹配标记degraded为true。2. 超时处理设置超时控制超时后返回部分结果并标记try:asyncwithasyncio.timeout(self.config.agent_timeout):treeawaitself.parser.parse(code,language)# ... 分析逻辑exceptTimeoutError:returnAnalysisResult(successTrue,failure_categoryFailureCategory.UPSTREAM_TIMEOUT,degradedTrue,error_message分析超时返回部分结果)3. 不支持的语言遇到不支持的语言时自动降级为正则匹配。使用示例基本用法agentStaticAnalyzerAgent(config)# 分析代码resultawaitagent.analyze_code( def complex_function(x): if x 0: for i in range(x): if i % 2 0: print(i) return x ,python)# 输出结果print(f发现{len(result.issues)}个问题)forissueinresult.issues:print(f[{issue.severity}] Line{issue.line}:{issue.description})作为Agent运行# 通过run方法执行taskjson.dumps({diff:code_content,language:python,files:[example.py]})resultawaitagent.run(task)outputjson.loads(result.output)性能优化异步执行所有耗时操作使用asyncio.to_thread避免阻塞事件循环。解析器复用解析器实例在Agent生命周期内复用避免重复初始化开销。正则预编译所有正则模式在模块加载时预编译提高匹配效率。