在 LlamaIndex 中调用 aevaluate_queries() 等异步方法时,必须将 await 语句置于 async 函数内部,并通过 asyncio.run() 启动事件循环;直接在模块顶层 await 会导致 SyntaxError,这是 Python 异步语法的硬性限制。
在 llamaindex 中调用 `aevaluate_queries()` 等异步方法时,必须将 `await` 语句置于 `async` 函数内部,并通过 `asyncio.run()` 启动事件循环;直接在模块顶层 `await` 会导致 syntaxerror,这是 python 异步语法的硬性限制。
Python 的 await 表达式只能出现在 async def 定义的协程函数中,这是语言层面的强制约束(参见 Python 官方文档)。因此,像以下写法会立即报错:
eval_results = await runner.aevaluate_queries(...) # ❌ SyntaxError: 'await' outside function
即使手动获取事件循环(如 asyncio.get_event_loop().run_until_complete(...)),在现代 Python(≥3.10)及某些运行环境(如部分 IDE 或脚本模式)中也容易触发 RuntimeError: coroutine raised StopIteration 等兼容性问题——这通常源于事件循环状态管理不当或嵌套调用冲突。
✅ 正确做法是:封装异步逻辑为协程函数,并统一使用 asyncio.run() 启动。该函数会自动创建并管理一个干净的事件循环,执行协程后关闭循环,是最安全、最推荐的顶层异步入口方式。
以下是完整可运行的修复示例:
import asyncio
from llama_index.core.evaluation import BatchEvalRunner, DatasetGenerator
# 假设已定义:documents, llm, vector_index, faithfulness_gpt4, relevancy_gpt4, qas
# 1. 生成评估数据集(同步)
dataset_generator = DatasetGenerator.from_documents(documents, llm=llm)
qas = dataset_generator.generate_dataset_from_nodes(num=3)
# 2. 初始化评估器
runner = BatchEvalRunner(
{"faithfulness": faithfulness_gpt4, "relevancy": relevancy_gpt4},
workers=8,
)
# 3. 定义协程函数(关键:await 必须在此内部)
async def run_batch_evaluation():
return await runner.aevaluate_queries(
vector_index.as_query_engine(llm=llm),
queries=qas.questions
)
# 4. 在顶层调用 asyncio.run —— 唯一允许的“启动点”
eval_results = asyncio.run(run_batch_evaluation())
⚠️ 注意事项:
- 不要重复调用 asyncio.get_event_loop() 或手动 loop.close():asyncio.run() 已内部处理,显式操作易引发异常;
- 若在 Jupyter Notebook 中运行,可直接 await(因 IPython 启用了 autoawait),但切勿将 Notebook 代码直接复制到 .py 脚本中——这是官方文档示例出错的常见原因;
- asyncio.run() 是单次调用,不可在已运行的事件循环中(如 async def 内部)再次调用,否则抛出 RuntimeError: asyncio.run() cannot be called from a running event loop。
总结:异步不是“加个 await 就行”,而是需遵循“协程定义 → 事件循环调度 → 结果返回”的标准流程。LlamaIndex 的 a* 方法均为此设计,严格遵守 Python 异步范式,才能稳定发挥并发评估的性能优势。