☰
用 RAG 对比评测 OpenAI o3 与 Claude 3.7 Sonnet:基于 GitHub 代码检索的端到端评测与可观测性实战
2026/10/17 12:58:47 网站建设 项目流程

用 RAG 对比评测 OpenAI o3 与 Claude 3.7 Sonnet:基于 GitHub 代码检索的端到端评测与可观测性实战

【免费下载链接】ai-engineering-hubIn-depth tutorials on LLMs, RAGs and real-world AI agent applications.项目地址: https://gitcode.com/GitHub_Trending/ai/ai-engineering-hub

导读

本项目以"对 GitHub 开源仓库做 RAG 检索增强问答"为统一基准,让 OpenAI o3-mini 与 Claude 3.7 Sonnet 两款模型回答同一批代码生成类问题,并借助 CometML Opik 构建从链路追踪(Tracing)到自动化评测(Evaluation)的端到端可观测流水线。读完本文,你将掌握:如何用 LlamaIndex 将 GitHub 仓库切分成可检索的代码节点、如何搭建可切换模型的 Streamlit 问答界面,以及如何用 Opik 的 LLM-as-a-Judge 指标对两个模型的生成结果做量化对比。

一、项目定位:为什么要在"代码 RAG"上做模型对比

o3-vs-claude-code目录围绕一个核心问题展开:针对真实开源仓库的代码问答场景,OpenAI o3-mini 与 Claude 3.7 Sonnet 谁的生成质量更高?官方 README 给出了明确定位——"Compare Claude 3.7 Sonnet and OpenAI o3 using RAG over code (GitHub)",同时强调该项目"leveraged CometML Opik to build an e2e evaluation and observability pipeline for a RAG application"(借助 Opik 为 RAG 应用构建端到端评测与可观测性流水线)。

换句话说,它把两件事结合在了一起:

  1. 一个可控的对比实验环境:同一个检索管线、同一份上下文、同一组问题,仅替换底层 LLM,从而把"模型能力差异"从"应用实现差异"中剥离出来;
  2. 一套可复现的评测机制:用带人工标注参考答案(ground truth)的测试集,配合 LLM 裁判打分,量化比较两个模型的输出质量,而不是靠肉眼观感下结论。

二、整体架构与工作流

从 app.py 与 Opik for LLM evaluation.ipynb 的源码结构看,整个链路可分为四个阶段:

GitHub URL → 克隆仓库 → 按文件类型解析与切分(CodeSplitter / MarkdownNodeParser) → 向量化(FastEmbed bge-base-en-v1.5) → 建索引(Qdrant / 内存) → 检索(similarity_top_k=4) → 流式问答(o3-mini 或 Claude 3.7 Sonnet) → Opik 追踪(LlamaIndexCallbackHandler) → LLM Judge 评测打分
  • 交互阶段由 Streamlit 应用承载(streamlit run app.py);
  • 评测阶段由 Notebook 承载(Opik for LLM evaluation.ipynb),评测数据在 data/test.csv 中。

两个阶段复用同一套解析、切分、索引与查询逻辑,保证了"评测的差异只来自模型本身"这一实验前提。

三、环境准备与安装

1. 获取 API Key

按 README 的说明,运行本项目需要三个密钥:

密钥用途
Opik API Key将追踪与评测结果上报到 Opik 平台(使用云端版时必需)
OpenAI API Key调用 o3-mini 模型,以及评测阶段作为裁判模型(gpt-4o)
Anthropic API Key调用 Claude 3.7 Sonnet

将这些密钥写入项目根目录的.env文件。README 中提示参考.env.example(当前仓库副本中未包含该文件,但结合源码中的load_dotenv()调用可推断,典型的键名形如OPIK_API_KEY、OPENAI_API_KEY、ANTHROPIC_API_KEY,app.py 第 29 行与 Notebook 均通过python-dotenv加载环境变量)。

2. 安装依赖

要求Python 3.11 或更高版本(Notebook 的元数据中也标注了 Python 3.11.11),README 给出的安装命令如下:

pip install opik llama-index llama-index-agent-openai llama-index-llms-openai llama-index-llms-anthropic --upgrade --quiet

除上述包外,从 app.py 的导入语句还可以看到实际运行还依赖以下组件:

  • streamlit(交互界面)、python-dotenv(环境变量)、nest_asyncio(Notebook 中异步兼容)、qdrant-client与llama-index-vector-stores-qdrant(Qdrant 向量库)、llama-index-embeddings-fastembed(本地嵌入模型)、llama-index-embeddings-huggingface(Notebook 中的备选嵌入方案)。

建议使用虚拟环境安装,避免与系统 Python 环境冲突。

四、运行交互式对比应用

安装完成后,在o3-vs-claude-code目录下启动:

streamlit run app.py

启动后在侧边栏可以完成三个关键操作(对应 app.py 第 102-192 行的侧边栏逻辑):

  1. 选择模型:在OpenAI o3-mini与Claude 3.7 Sonnet之间切换(第 105-108 行的model_options字典);
  2. 输入 GitHub 仓库 URL:例如https://github.com/Lightning-AI/LitServe;
  3. 点击 Load:触发克隆、解析、建索引、构建查询引擎的完整流程。

页面顶部标题即为 "Claude 3.7 Sonnet vs OpenAI o3!",并在右侧提供 "Clear ↺" 按钮一键清空会话(对应reset_chat()函数)。

五、核心实现剖析:app.py 逐模块解读

1. 模型抽象:一个函数支持双 Provider

@st.cache_resource def load_llm(model_name, provider="openai"): if provider == "anthropic": return Anthropic(model=model_name) elif provider == "openai": return OpenAI(model=model_name) else: raise ValueError(f"Unsupported provider: {provider}")

(app.py 第 32-39 行)

@st.cache_resource保证同一模型实例在多次交互中复用,避免重复初始化造成的资源浪费。这是实现"同一个 RAG 管线、仅换模型"的关键抽象点——后续只需在Settings.llm上覆盖赋值,即可无缝切换推理后端。

2. GitHub URL 解析与仓库克隆

def parse_github_url(url): pattern = r"https://github\.com/([^/]+)/([^/]+)" match = re.match(pattern, url) return match.groups() if match else (None, None) def validate_owner_repo(owner, repo): return bool(owner) and bool(repo)

(app.py 第 43-53 行)

URL 解析通过正则提取owner与repo,随后调用git clone将仓库克隆到当前工作目录(第 130-131 行:若本地目录./{repo}不存在则克隆,已存在则直接复用)。克隆失败或 URL 不合法时,界面会分别提示 "Error occurred while cloning the repository, carefully check the url" 与 "Invalid owner or repository"。

3. 按文件类型解析与切分(代码 RAG 的关键)

def parse_docs_by_file_types(ext, language, input_dir_path): files = glob.glob(f"{input_dir_path}/**/*{ext}", recursive=True) if len(files) > 0: loader = SimpleDirectoryReader( input_dir=input_dir_path, required_exts=[ext], recursive=True ) docs = loader.load_data() parser = ( MarkdownNodeParser() if ext == ".md" else CodeSplitter.from_defaults(language=language) ) nodes = parser.get_nodes_from_documents(docs) return nodes return []

(app.py 第 55-73 行)

应用默认处理五类文件(第 134-140 行):

扩展名解析器语言参数
.mdMarkdownNodeParser—(按 Markdown 结构切分)
.pyCodeSplitter.from_defaults(language="python")python
.ipynbCodeSplitterpython
.jsCodeSplitterjavascript
.tsCodeSplittertypescript

值得说明的是,代码文件与自然语言文档的切分策略完全不同:CodeSplitter会尽量在类、函数等逻辑边界处切分,保留代码的语义完整性;MarkdownNodeParser则按标题层级组织节点。这正是"对代码仓库做 RAG"区别于普通文档 RAG 的核心技术点。每个扩展名处理完成后会打印Found N files ... / Processed N nodes ...日志,便于排查仓库是否有可检索内容。

4. 向量化与索引构建

Settings.embed_model = FastEmbedEmbedding(model_name="BAAI/bge-base-en-v1.5") try: index = create_index(nodes) # Qdrant 向量库路径 except: index = VectorStoreIndex(nodes=nodes) # 内存索引兜底

(app.py 第 150-154 行)

嵌入模型采用BAAI/bge-base-en-v1.5(通过FastEmbedEmbedding本地运行,无需单独部署 Embedding 服务)。索引构建有两条路径:

  • 首选 Qdrant:create_index()(第 77-86 行)会为每个会话生成一个uuid4()唯一集合名(chat_with_docs_{uuid}),通过QdrantVectorStore与StorageContext写入向量库,天然支持会话隔离与持久化;
  • 兜底内存索引:从源码结构看,app.py 第 152 行调用create_index(nodes)时未传入client参数(而函数签名要求该参数),因此该调用会抛出TypeError并被except捕获,实际大多走VectorStoreIndex(nodes=nodes)的内存索引路径;Notebook 中的评测流程则直接使用内存索引。这一点体现了"可插拔存储"的设计:向量库不可用时应用仍可运行。

5. 检索与生成参数

query_engine = index.as_query_engine(streaming=True, similarity_top_k=4)

(app.py 第 159 行)

  • streaming=True:开启流式输出,前端逐 token 渲染;
  • similarity_top_k=4:检索阶段取与问题最相似的 4 个节点作为上下文注入 Prompt。

6. 自定义 Prompt 模板

qa_prompt_tmpl_str = ( "Context information is below.\n" "---------------------\n" "{context_str}\n" "---------------------\n" "Given the context information and your knowledge, I want you to think step by step " "to answer the query in a crisp manner, incase case you don't know the answer say 'I don't know!'.\n" "Query: {query_str}\n" "Answer: " ) qa_prompt_tmpl = PromptTemplate(qa_prompt_tmpl_str) query_engine.update_prompts( {"response_synthesizer:text_qa_template": qa_prompt_tmpl} )

(app.py 第 162-175 行)

该模板要求模型基于上下文"step by step"思考并简洁作答,无法回答时明确输出I don't know!——这一约束对评测尤其重要,可以抑制模型的幻觉式作答。而 Notebook 中的评测版模板更进一步(见下文第八节),强制要求回答必须包含代码片段,与"代码生成评测"的定位完全对齐。

7. 流式聊天界面

streaming_response = query_engine.query(prompt) for chunk in streaming_response.response_gen: full_response += chunk message_placeholder.markdown(full_response + "▌") message_placeholder.markdown(full_response)

(app.py 第 231-239 行)

用户输入通过st.chat_input捕获,追加到st.session_state.messages维护会话历史;回答期间用▌光标模拟打字效果,完成后将完整回答写回历史记录。reset_chat()则负责清空messages与context并触发gc.collect()释放内存。

六、评测数据集:data/test.csv

评测样本存放在 data/test.csv,采用question,answer两列结构:question是待评测问题,answer是人工编写/认可的参考答案(ground truth)。从数据内容看,测试集围绕LitServe 框架(Lightning AI 的推理服务库)设计,覆盖了五种典型场景:

  1. SimpleLitAPI:输入一个数,返回平方与立方的和(server.py完整示例);
  2. 文本嵌入 API:基于 SentenceTransformer(BAAI/bge-large-en-v1.5)+ LitServe 构建;
  3. RAG API:LlamaIndex + Qdrant 向量库 + Ollama 本地 llama3.2 的组合;
  4. Whisper 私有 API:将 OpenAI Whisper(large模型、GPU 加速)封装为推理服务;
  5. 随机森林部署:加载model.pkl并封装为分类 API。

设计精巧之处在于:所有问题都是"描述性需求 + 具体约束"(如指定模型、端口、批大小、加速器等),答案要求产出完整可运行的代码。这种设定对模型的能力区分度很高——不仅能检验"是否理解需求",还能检验"是否产出符合约束的语法正确代码",天然适配 LLM-as-a-Judge 的自动打分。

七、用 Opik 构建端到端可观测性

1. 初始化与配置

import opik opik.configure(use_local=False)

(Notebook 第一个代码单元)

use_local=False表示使用 Opik 云端(需在.env中配置 Opik API Key);若要本地部署 Opik 服务,可改为use_local=True并指向本地地址。

2. 追踪 RAG 全链路调用

from llama_index.core.callbacks import CallbackManager from opik.integrations.llama_index import LlamaIndexCallbackHandler opik_callback_handler = LlamaIndexCallbackHandler() Settings.callback_manager = CallbackManager([opik_callback_handler])

这是整个可观测性体系的基石:LlamaIndexCallbackHandler会自动把LlamaIndex 内部所有操作(文档加载、节点切分、embedding、检索、LLM 调用、响应合成)以 Trace/Span 的形式上报到 Opik。开发者无需在业务代码中埋点,就能在 Opik 平台看到一次问答从"检索到哪 4 个节点"到"模型生成了什么"的完整调用链,极大降低了 RAG 应用的调试成本。

3. 在评测中复用同一套 RAG 管线

Notebook 中的setup_chat_engine(github_url, model_provider=...)与 app.py 高度同构:解析 URL → 克隆 → 按文件类型切分 → 建索引 → 配置 LLM → 构建streaming=True, similarity_top_k=4的查询引擎。值得注意的两点差异:

  • 支持Claude 3.5 Sonnet(claude-3-5-sonnet-20240620)作为第三个可选项;
  • 评测版 Prompt 强制要求代码输出:
Given the context information above, you must always include a code snippet in your response. Think step by step to answer the query, and then provide a relevant code example that demonstrates the concept. ... If you don't know the answer, say 'I don't know!' but still provide a minimal code example of what you think might work.

Notebook 示例中以https://github.com/Lightning-AI/LitServe作为评测仓库(与 test.csv 的题目主题一致),调用方式为:

model_name = 'Claude 3.7 Sonnet' query_engine = setup_chat_engine(github_url, model_provider=model_name) response = query_engine.query("What is this repo about?")

八、LLM-as-a-Judge:自动化评测实现

1. 创建评测数据集

from opik import Opik client = Opik() dataset = client.get_or_create_dataset(name="Eval Code Generation")

评测前需要把 test.csv 中的问答对导入该数据集(Notebook 已创建名为Eval Code Generation的数据集)。

2. 包装评测任务

from opik import track @track def my_llm_application(input: str) -> str: response = query_engine.query(input) return str(response) def evaluation_task(x): return {"output": my_llm_application(x['input'])}

@track装饰器让每次评测调用也被记录到 Opik,实现"评测本身可观测"。

3. 自定义 LLM Judge 指标

Notebook 定义了一个完整的LLMJudgeMetric类(继承opik.evaluation.metrics.base_metric.BaseMetric),核心逻辑如下:

  • 裁判模型:默认gpt-4o(通过openai客户端调用);
  • 打分维度:正确性(是否实现相同功能)、完整性(是否包含必要组件)、效率(实现方式是否同样高效);并明确"只要功能等价就给高分"、"只关注代码与功能,忽略文本";
  • 输出格式:强制要求裁判返回严格的 JSON:{"score": <0~1之间的数>},其中 0 表示完全不相关/错误,1 表示与参考答案功能等价;
  • 落库:通过score_result.ScoreResult(name=..., value=...)将分数封装为 Opik 指标。
code_quality_metric = LLMJudgeMetric()

4. 运行评测实验

from opik.evaluation import evaluate evaluation = evaluate( dataset=dataset, task=evaluation_task, experiment_name=model_name, # 用模型名区分实验 scoring_metrics=[code_quality_metric], experiment_config={"model": "gpt-3.5-turbo"} )

这就是整个对比实验的"收敛点":为每个模型各跑一次evaluate,experiment_name分别设为 "OpenAI o3-mini" 与 "Claude 3.7 Sonnet",即可在 Opik 平台同一数据集上并排对比两个模型的平均分、逐样本得分分布与失败案例。experiment_config记录实验元信息(如推理模型),保证实验可复现、可追溯。

九、对比实验的设计要点与落地建议

综合整个项目,这套"代码 RAG + 双模型对比 + Opik 评测"的方案在工程上有几个值得借鉴的设计决策:

  1. 控制变量:解析、切分、嵌入、检索、Prompt 全部固定,唯一变量是底层 LLM,使得分差异能归因到模型本身;
  2. 面向代码的评测口径:测试题要求产出完整可运行代码,裁判按"功能等价性"而非"文本相似度"打分,避免传统 ROUGE/BLEU 在代码场景的失效问题;
  3. 评测与交互解耦:交互看效果用 Streamlit(app.py),批量量化用 Notebook(Opik for LLM evaluation.ipynb),两者共享核心管线代码,可独立演进;
  4. 可观测性贯穿始终:追踪(Callback Handler)与评测(Judge 指标、实验管理)都收敛到 Opik 单一平台,链路视图 + 得分视图 + 数据集管理一站式完成。

注意事项与前提:本方案依赖 OpenAI、Anthropic 与 Opik 三方服务的 API Key,且需要能访问外网以克隆仓库与下载模型/依赖;嵌入模型与切分参数(similarity_top_k=4)对结果有影响,若要横向对比其他评测集,建议保持这些参数一致并记录在experiment_config中。

十、在仓库中继续深入

如果你想进一步研究或复用本方案,可在仓库内继续查看以下文件:

  • o3-vs-claude-code/app.py:Streamlit 交互应用完整实现(模型抽象、仓库解析、代码切分、向量检索、流式问答);
  • o3-vs-claude-code/Opik for LLM evaluation.ipynb:Opik 追踪与 LLM Judge 评测的完整 Notebook(含自定义指标类与evaluate调用);
  • o3-vs-claude-code/data/test.csv:评测数据集(问题 + 参考答案),可自行扩充更多代码生成场景;
  • o3-vs-claude-code/data_prep.ipynb:数据准备 Notebook(当前为空,可结合 data/test.csv 自行补充生成逻辑);
  • 仓库中还有多个可交叉参考的项目:deploy-agentic-rag(LitServe 部署 RAG API)、github-rag(本地代码仓库问答)、eval-and-observability(Opik 评测与可观测性专项)等,可与本项目的评测管线相互印证。

通过本项目的实践,你将同时掌握两条硬技能:基于 LlamaIndex 对代码仓库构建 RAG,以及用 Opik 搭建"追踪 + 评测 + 实验对比"的端到端可观测体系——这两者正是当前 RAG 应用从原型走向生产的必备能力。

【免费下载链接】ai-engineering-hubIn-depth tutorials on LLMs, RAGs and real-world AI agent applications.项目地址: https://gitcode.com/GitHub_Trending/ai/ai-engineering-hub

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询