一次在 Mac 上从零开始搭建 llama-index 的开发经历

Qiang 发布在技术 1

开发环境

安装 llama-index

官方教程:https://github.com/jerryjliu/llama_index#-example-usage

pip install llama-index
具体执行

pip3 install llama-index

$ pip3 install llama-index
Collecting llama-index
  Downloading https://files.pythonhosted.org/packages/eb/7b/59bc2ab39140edeb842e9ff8df5c7604744592d5369539273d290bb5df54/llama_index-0.5.1.tar.gz (160kB)
    100% |████████████████████████████████| 163kB 635kB/s 
Collecting dataclasses_json (from llama-index)
  Downloading https://files.pythonhosted.org/packages/58/7e/2042610dfc8121e8119ad8b94db496d8697e4b0ef7a6e378018a2bd84435/dataclasses_json-0.5.7-py3-none-any.whl
Collecting langchain (from llama-index)
  Downloading https://files.pythonhosted.org/packages/1f/fd/f2aa39f8e63a6fbacf2e7be820b846c27b1e5830af9c2e2e208801b6c07f/langchain-0.0.27-py3-none-any.whl (124kB)
    100% |████████████████████████████████| 133kB 1.5MB/s 
Collecting numpy (from llama-index)
  Downloading https://files.pythonhosted.org/packages/6a/9d/984f87a8d5b28b1d4afc042d8f436a76d6210fb582214f35a0ea1db3be66/numpy-1.19.5-cp36-cp36m-macosx_10_9_x86_64.whl (15.6MB)
    100% |████████████████████████████████| 15.6MB 77kB/s 
Collecting tenacity<9.0.0,>=8.2.0 (from llama-index)
  Downloading https://files.pythonhosted.org/packages/e7/b0/c23bd61e1b32c9b96fbca996c87784e196a812da8d621d8d04851f6c8181/tenacity-8.2.2-py3-none-any.whl
Collecting openai>=0.26.4 (from llama-index)
  Could not find a version that satisfies the requirement openai>=0.26.4 (from llama-index) (from versions: 0.0.2, 0.1.0, 0.1.1, 0.1.2, 0.1.3, 0.2.0, 0.2.1, 0.2.3, 0.2.4, 0.2.5, 0.2.6, 0.3.0, 0.4.0, 0.6.0, 0.6.1, 0.6.2, 0.6.3, 0.6.4, 0.7.0, 0.8.0, 0.9.0, 0.9.1, 0.9.2, 0.9.3, 0.9.4, 0.10.0, 0.10.1, 0.10.2, 0.10.3, 0.10.4, 0.10.5)
No matching distribution found for openai>=0.26.4 (from llama-index)

执行失败,提示:Could not find a version that satisfies the requirement...,Google 搜索,得到这个问答:Pip could not find a version that satisfies the requirement,以下是答案简要:

pip doesn't find anything to install because you do not meet the requirements.

Upgrade to Python >= 3.8

按提示升级 python 到 3.8 或以上。

执行日志

brew upgrade python3

$ brew upgrade python3
Running `brew update --auto-update`...
==> Auto-updated Homebrew!
Updated 2 taps (homebrew/core and homebrew/cask).
==> New Formulae
dexter                                                 typst
==> New Casks
hummingbird

You have 9 outdated formulae installed.
You can upgrade them with brew upgrade
or list them with brew outdated.

Warning: You are using macOS 10.15.
We (and Apple) do not provide support for this old version.
It is expected behaviour that some formulae will fail to build in this old version.
It is expected behaviour that Homebrew will be buggy and slow.
Do not create any issues about this on Homebrew's GitHub repositories.
Do not create any issues even if you think this message is unrelated.
Any opened issues will be immediately closed without response.
Do not ask for help from Homebrew or its maintainers on social media.
You may ask for help in Homebrew's discussions but are unlikely to receive a response.
Try to figure out the problem yourself and submit a fix as a pull request.
We will review it but may or may not accept it.

Warning: python3 3.11.2_1 already installed

提示 python3 3.11.2_1 already installed,查看当前已经安装的 python 版本和位置:

执行日志

控制台

$ python3 --version
Python 3.6.1
$ python3.11 --version
-bash: python3.11: command not found
$ which python3
/Library/Frameworks/Python.framework/Versions/3.6/bin/python3
$ which python3.11
$ python --version
Python 2.7.16
$ which python
/usr/bin/python
$ brew list --versions | grep python
python@3.11 3.11.2_1
python@3.8 3.8.16
$ brew info python@3.11
==> python@3.11: stable 3.11.2
Interpreted, interactive, object-oriented programming language
https://www.python.org/
/usr/local/Cellar/python@3.11/3.11.2_1 (8,121 files, 193.6MB)
  Built from source on 2023-03-28 at 11:49:01
From: https://github.com/Homebrew/homebrew-core/blob/HEAD/Formula/python@3.11.rb
License: Python-2.0
==> Dependencies
Build: pkg-config ✔
Required: mpdecimal ✔, openssl@1.1 ✔, sqlite ✔, xz ✔
==> Caveats
Python has been installed as
  /usr/local/bin/python3

Unversioned symlinks `python`, `python-config`, `pip` etc. pointing to
`python3`, `python3-config`, `pip3` etc., respectively, have been installed into
  /usr/local/opt/python@3.11/libexec/bin

You can install Python packages with
  pip3 install <package>
They will install into the site-package directory
  /usr/local/lib/python3.11/site-packages

tkinter is no longer included with this formula, but it is available separately:
  brew install python-tk@3.11

gdbm (`dbm.gnu`) is no longer included in this formula, but it is available separately:
  brew install python-gdbm@3.11
`dbm.ndbm` changed database backends in Homebrew Python 3.11.
If you need to read a database from a previous Homebrew Python created via `dbm.ndbm`,
you'll need to read your database using the older version of Homebrew Python and convert to another format.
`dbm` still defaults to `dbm.gnu` when it is installed.

For more information about Homebrew and Python, see: https://docs.brew.sh/Homebrew-and-Python


==> Analytics
install: 5,909 (30 days), 1,010,311 (90 days), 1,678,276 (365 days)
install-on-request: 1,408 (30 days), 157,665 (90 days), 191,482 (365 days)
build-error: 3 (30 days)

通过把以上内容给 ChatGPT 并问 如何把 python3.11 变更为默认的 python 版本?,得到以下答案:

可以使用以下命令将 Python 3.11 设置为默认版本:

echo 'export PATH="/usr/local/opt/python@3.11/bin:$PATH"' >> ~/.bash_profile
source ~/.bash_profile

这将把 Python 3.11 的路径添加到您的 bash 配置文件中,并使其成为默认版本。您可以通过运行 python --version 来验证默认版本是否已更改为 Python 3.11。

复制 ChatGPT 提供的代码,再次查看 Python3 的版本,得到的是 Python 3.11.2

如果还是原版本,可以打开 ~/.bash_profile 并注释/移除原版本的Python 路径。

再次执行 pip3 install llama-index 安装 llama-index

执行日志

pip3 install llama-index

$ pip3 install llama-index
Collecting llama-index
  Using cached llama_index-0.5.1.tar.gz (160 kB)
  Preparing metadata (setup.py) ... done
Collecting dataclasses_json
  Using cached dataclasses_json-0.5.7-py3-none-any.whl (25 kB)
Collecting langchain
  Downloading langchain-0.0.126-py3-none-any.whl (450 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 450.7/450.7 kB 669.6 kB/s eta 0:00:00
Collecting numpy
  Downloading numpy-1.24.2-cp311-cp311-macosx_10_9_x86_64.whl (19.8 MB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 19.8/19.8 MB 1.2 MB/s eta 0:00:00
Collecting tenacity<9.0.0,>=8.2.0
  Using cached tenacity-8.2.2-py3-none-any.whl (24 kB)
Requirement already satisfied: openai>=0.26.4 in /usr/local/lib/python3.11/site-packages (from llama-index) (0.27.2)
Collecting pandas
  Downloading pandas-1.5.3-cp311-cp311-macosx_10_9_x86_64.whl (11.9 MB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 11.9/11.9 MB 1.2 MB/s eta 0:00:00
Collecting tiktoken
  Downloading tiktoken-0.3.3-cp311-cp311-macosx_10_9_x86_64.whl (735 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 735.6/735.6 kB 672.7 kB/s eta 0:00:00
Requirement already satisfied: requests>=2.20 in /usr/local/lib/python3.11/site-packages (from openai>=0.26.4->llama-index) (2.28.2)
Requirement already satisfied: tqdm in /usr/local/lib/python3.11/site-packages (from openai>=0.26.4->llama-index) (4.65.0)
Requirement already satisfied: aiohttp in /usr/local/lib/python3.11/site-packages (from openai>=0.26.4->llama-index) (3.8.4)
Collecting marshmallow<4.0.0,>=3.3.0
  Downloading marshmallow-3.19.0-py3-none-any.whl (49 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 49.1/49.1 kB 992.7 kB/s eta 0:00:00
Collecting marshmallow-enum<2.0.0,>=1.5.1
  Downloading marshmallow_enum-1.5.1-py2.py3-none-any.whl (4.2 kB)
Collecting typing-inspect>=0.4.0
  Downloading typing_inspect-0.8.0-py3-none-any.whl (8.7 kB)
Collecting PyYAML>=5.4.1
  Downloading PyYAML-6.0-cp311-cp311-macosx_10_9_x86_64.whl (188 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 188.6/188.6 kB 1.2 MB/s eta 0:00:00
Collecting SQLAlchemy<2,>=1
  Downloading SQLAlchemy-1.4.47-cp311-cp311-macosx_10_9_universal2.whl (1.6 MB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 1.6/1.6 MB 1.3 MB/s eta 0:00:00
Collecting boto3<2.0.0,>=1.26.96
  Downloading boto3-1.26.102-py3-none-any.whl (135 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 135.6/135.6 kB 534.2 kB/s eta 0:00:00
Collecting pydantic<2,>=1
  Downloading pydantic-1.10.7-cp311-cp311-macosx_10_9_x86_64.whl (2.8 MB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 2.8/2.8 MB 1.3 MB/s eta 0:00:00
Collecting pyowm<4.0.0,>=3.3.0
  Downloading pyowm-3.3.0-py3-none-any.whl (4.5 MB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 4.5/4.5 MB 1.2 MB/s eta 0:00:00
Collecting python-dateutil>=2.8.1
  Using cached python_dateutil-2.8.2-py2.py3-none-any.whl (247 kB)
Collecting pytz>=2020.1
  Using cached pytz-2023.3-py2.py3-none-any.whl (502 kB)
Collecting regex>=2022.1.18
  Downloading regex-2023.3.23-cp311-cp311-macosx_10_9_x86_64.whl (294 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 294.5/294.5 kB 1.6 MB/s eta 0:00:00
Requirement already satisfied: attrs>=17.3.0 in /usr/local/lib/python3.11/site-packages (from aiohttp->openai>=0.26.4->llama-index) (22.2.0)
Requirement already satisfied: charset-normalizer<4.0,>=2.0 in /usr/local/lib/python3.11/site-packages (from aiohttp->openai>=0.26.4->llama-index) (3.1.0)
Requirement already satisfied: multidict<7.0,>=4.5 in /usr/local/lib/python3.11/site-packages (from aiohttp->openai>=0.26.4->llama-index) (6.0.4)
Requirement already satisfied: async-timeout<5.0,>=4.0.0a3 in /usr/local/lib/python3.11/site-packages (from aiohttp->openai>=0.26.4->llama-index) (4.0.2)
Requirement already satisfied: yarl<2.0,>=1.0 in /usr/local/lib/python3.11/site-packages (from aiohttp->openai>=0.26.4->llama-index) (1.8.2)
Requirement already satisfied: frozenlist>=1.1.1 in /usr/local/lib/python3.11/site-packages (from aiohttp->openai>=0.26.4->llama-index) (1.3.3)
Requirement already satisfied: aiosignal>=1.1.2 in /usr/local/lib/python3.11/site-packages (from aiohttp->openai>=0.26.4->llama-index) (1.3.1)
Collecting botocore<1.30.0,>=1.29.102
  Downloading botocore-1.29.102-py3-none-any.whl (10.6 MB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 10.6/10.6 MB 1.2 MB/s eta 0:00:00
Collecting jmespath<2.0.0,>=0.7.1
  Downloading jmespath-1.0.1-py3-none-any.whl (20 kB)
Collecting s3transfer<0.7.0,>=0.6.0
  Downloading s3transfer-0.6.0-py3-none-any.whl (79 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 79.6/79.6 kB 1.0 MB/s eta 0:00:00
Collecting packaging>=17.0
  Downloading packaging-23.0-py3-none-any.whl (42 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 42.7/42.7 kB 888.4 kB/s eta 0:00:00
Collecting typing-extensions>=4.2.0
  Downloading typing_extensions-4.5.0-py3-none-any.whl (27 kB)
Collecting geojson<3,>=2.3.0
  Downloading geojson-2.5.0-py2.py3-none-any.whl (14 kB)
Collecting PySocks<2,>=1.7.1
  Downloading PySocks-1.7.1-py3-none-any.whl (16 kB)
Collecting six>=1.5
  Using cached six-1.16.0-py2.py3-none-any.whl (11 kB)
Requirement already satisfied: idna<4,>=2.5 in /usr/local/lib/python3.11/site-packages (from requests>=2.20->openai>=0.26.4->llama-index) (3.4)
Requirement already satisfied: urllib3<1.27,>=1.21.1 in /usr/local/lib/python3.11/site-packages (from requests>=2.20->openai>=0.26.4->llama-index) (1.26.15)
Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.11/site-packages (from requests>=2.20->openai>=0.26.4->llama-index) (2022.12.7)
Collecting greenlet!=0.4.17
  Downloading greenlet-2.0.2-cp311-cp311-macosx_10_9_universal2.whl (243 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 243.0/243.0 kB 1.1 MB/s eta 0:00:00
Collecting mypy-extensions>=0.3.0
  Downloading mypy_extensions-1.0.0-py3-none-any.whl (4.7 kB)
Building wheels for collected packages: llama-index
  Building wheel for llama-index (setup.py) ... done
  Created wheel for llama-index: filename=llama_index-0.5.1-py3-none-any.whl size=241531 sha256=f2b89485b13728ce5301fde5afbc96ca6335727e3e903540454120948c63e553
  Stored in directory: /Users/chshqiang/Library/Caches/pip/wheels/cf/d0/ea/4a81ca217a7bf173dcd11de1005f67f9a9d291343f97010171
Successfully built llama-index
Installing collected packages: pytz, geojson, typing-extensions, tenacity, six, regex, PyYAML, PySocks, packaging, numpy, mypy-extensions, jmespath, greenlet, typing-inspect, tiktoken, SQLAlchemy, python-dateutil, pydantic, marshmallow, pyowm, pandas, marshmallow-enum, botocore, s3transfer, dataclasses_json, boto3, langchain, llama-index
Successfully installed PySocks-1.7.1 PyYAML-6.0 SQLAlchemy-1.4.47 boto3-1.26.102 botocore-1.29.102 dataclasses_json-0.5.7 geojson-2.5.0 greenlet-2.0.2 jmespath-1.0.1 langchain-0.0.126 llama-index-0.5.1 marshmallow-3.19.0 marshmallow-enum-1.5.1 mypy-extensions-1.0.0 numpy-1.24.2 packaging-23.0 pandas-1.5.3 pydantic-1.10.7 pyowm-3.3.0 python-dateutil-2.8.2 pytz-2023.3 regex-2023.3.23 s3transfer-0.6.0 six-1.16.0 tenacity-8.2.2 tiktoken-0.3.3 typing-extensions-4.5.0 typing-inspect-0.8.0
WARNING: There was an error checking the latest version of pip.

Successfully built llama-index

安装成功。

也可以先安装 openai 再安装 llama-index

使用 llama-index

官方文档的例子:

import os
os.environ["OPENAI_API_KEY"] = 'YOUR_OPENAI_API_KEY'

from llama_index import GPTSimpleVectorIndex, SimpleDirectoryReader
documents = SimpleDirectoryReader('data').load_data()
index = GPTSimpleVectorIndex.from_documents(documents)

在命令行里输入 python3 ,然后输入并执行以上程序。

输出:

INFO:llama_index.token_counter.token_counter:> [build_index_from_nodes] Total LLM token usage: 0 tokens
INFO:llama_index.token_counter.token_counter:> [build_index_from_nodes] Total embedding token usage: 0 tokens

0 tokens? 这是正常的,因为没有任何数据被使用。注意第四行SimpleDirectoryReader('data')SimpleDirectoryReader 是一个简单的目录读取器,可以读取指定目录下的文件,并将它们转换成文档。可以选择读取单个文件或者将多个文件合并成一个文档。可以通过传入参数来控制读取的文件类型、是否递归读取子目录、是否排除隐藏文件等。同时也可以通过传入一个函数来为每个文档添加元数据。

参数列表

参数说明

  1. input_dir (str) - 目录路径。
  2. input_files (List) - 要读取的文件路径列表(可选;覆盖input_dir,exclude)。
  3. exclude (List) - 要排除的Python文件路径的glob(可选)。
  4. exclude_hidden (bool) - 是否排除隐藏文件(点文件)。
  5. errors (str) - 如何处理编码和解码错误,请参见https://docs.python.org/3/library/functions.html#open。
  6. recursive (bool) - 是否递归搜索子目录。默认为False。
  7. required_exts (Optional[List[str]]) - 必需扩展名的列表。默认为None。
  8. file_extractor (Optional[Dict[str, BaseParser]]) - 将文件扩展名映射到BaseParser类的映射,该类指定如何将该文件转换为文本。请参见DEFAULT_FILE_EXTRACTOR。
  9. num_files_limit (Optional[int]) - 要读取的最大文件数。默认为None。
  10. file_metadata (Optional[Callable[str, Dict]]) - 一个函数,它接受文件名并返回Document的元数据字典。默认为None。

所以这里的 data 应是一个可访问的文档目录。

在本地创建一个文件夹,取名 data,从网上一本名著,比如《红楼梦》,下载到 data 文件夹中。

注意,红楼梦内容很大,需要消耗很多 Tokens,请谨慎使用,或使用较小的内容测试。

再执行上面的代码,输出:

INFO:llama_index.token_counter.token_counter:> [build_index_from_nodes] Total LLM token usage: 0 tokens
INFO:llama_index.token_counter.token_counter:> [build_index_from_nodes] Total embedding token usage: 1868673 tokens

Total embedding token usage: 1868673 tokens,这表示 openai/embedding api 消耗了 1868673 个 tokens,大约 0.75美元($0.0004 per 1000 tokens)。

然后我们保存结果到磁盘并从磁盘中加载(见官方文档):

# save to disk
index.save_to_disk('index.json')
# load from disk
index = GPTSimpleVectorIndex.load_from_disk('index.json')

执行第一行代码时,index.json 会被保存到当前目录(与 data 文件夹所在目录),这是一个很大的文件,大约 43.4MB,其内容格式大致如下:

直接下载我的红楼梦_index.json.zip

index.json

Data embeddings response

{
  "data": [
    {
      "embedding": [
        -0.006929283495992422,
        -0.005336422007530928,
        ...
        -4.547132266452536e-05,
        -0.024047505110502243
      ],
      "index": 0,
      "object": "embedding"
    }
  ],
  "model": "text-embedding-ada-002",
  "object": "list",
  "usage": {
    "prompt_tokens": 5,
    "total_tokens": 5
  }
}

可以在这里查看具体的工作流程及介绍:How to get embeddings

最后我们开始查询:

注意,查询也是需要消耗 tokens 的,不过比较少。

查询时,得把我们的查询数据发送给 openai/embeddings 接口,获取查询数据的向量列表(此处会消耗你的 tokens)然后 LlamaIndex 会计算《红楼梦》所有的向量与查询向量之间的距离,距离越小内容相关性越高,最终取相关性最高的几个向量,并从 openai 处获取这几个向量所映射的内容(此处会消耗你的 tokens)。

index.query("贾宝玉是谁?")
执行日志

INFO:llama_index.token_counter.token_counter:> [query] Total LLM token usage: 3371 tokens
INFO:llama_index.token_counter.token_counter:> [query] Total embedding token usage: 13 tokens
Response(response='\n贾宝玉是一个小说中的人物,他是《红楼梦》中的主要角色之一。', source_nodes=[SourceNode(source_text='\u3000\u3000少刻,宝玉出席解手,蒋玉函随着出来,二人站在廊搪下,蒋玉函又赔不是。宝玉见他抚媚温柔,心中十分留恋,便紧紧的攥着他的手,叫他:“闲了往我们那里去。还有一句话问你,也是你们贵班中,有一个叫琪官儿的,他如今名驰天下,可惜我独无缘一见。”蒋玉函笑道:“就是我的小名儿。”宝玉听说,不觉欣然跌足笑道:“有幸,有幸!果然名不虚传。今儿初会,却怎么样呢?”想了一想,向袖中取出扇子,将一个玉块扇坠解下来,递给琪官,道:“微物不堪,略表今日之谊。”琪官接了,笑道:“无功受禄,何以克当?也罢,我这里也得了一件奇物,今日早起才系上,还是簇新,聊可表我一点亲热之意。”说毕撩衣,将系小衣儿的一条大红汗巾子解下来递给宝玉道:“这汗巾子是茜香国女国王所贡之物,夏天系着肌肤生香,不生汗渍。昨日北静王给的,今日才上身。若是别人,我断不肯相赠。二爷请把自己系的解下来给我系着。”宝玉听说,喜不自禁,连忙接了,将自己一条松花汗巾解下来递给棋官。二人方束好,只听一声大叫:“我可拿住了!”只见薛蟠跳出来,拉着二人道:“放着酒不喝,两个人逃席出来,干什么?快拿出来我瞧瞧。”二人都道:“没有什么。”薛蟠那里肯依,还是冯紫英出来才解开了。复又归坐饮酒,至晚方散。 \u3000\u3000宝玉回至园中,宽衣吃茶,袭人见扇上的坠儿没了,便问他:“往那里去了?”宝玉道:“马上丢了。”袭人也不理论。及睡时,见他腰里一条血点似的大红汗巾子,便猜着了八九分,因说道:“你有了好的系裤子了,把我的那条还我罢。”宝玉听说,方想起那汗巾子原是袭人的,不该给人。心里后悔,口里说不出来,只得笑道:“我赔你一条罢。”袭人听了,点头叹道:“我就知道你又干这些事了,也不该拿我的东西给那些混账人哪。也难为你心里没个算计儿!”还要说几句,又恐怄上他的酒来,少不得也睡了。一宿无语。 \u3000\u3000次日天明方醒,只见宝玉笑道:“夜里失了盗也不知道,你瞧瞧裤子上。”袭人低头一看,只见昨日宝玉系的那条汗巾子,系在自己腰里了,便知是宝玉夜里换的,忙一顿就解下来,说道:“我不希罕这行子,趁早儿拿了去。”宝玉见他如此,只得委婉解劝了一回。袭人无法,暂且系上。 \u3000\u3000过后宝玉出去,终久解下来,扔在个空箱子里了,自己又换了一条系着。宝玉并未理论。因问起:“昨日可有什么事情?”袭人便回说:“二奶奶打发人叫了小红去了。他原要等你来着,我想什么要紧,我就做了主,打发他去了。”宝玉道:“很是。我已经知道了,不必等我罢了。”袭人又道:“昨儿贵妃打发夏太监出来送了一百二十两银子,叫在清虚观初一到初三打三天平安醮,唱戏献供,叫珍大爷领着众位爷们跪香拜佛呢。还有端午儿的节礼也赏了。”说着,命小丫头来,将昨日的所赐之物取出来,却是上等宫扇两柄,红麝香珠二串,凤尾罗二端,芙蓉簟一领。宝玉见了,喜不自胜,问:“别人的也都是这个吗?”袭人道:“老太太多着一个香玉如意,一个玛瑙枕。老爷、太太、姨太太的,只多着一个香玉如意。你和宝姑娘的一样。林姑娘和二姑娘、三姑娘、四姑娘只单有扇子和数珠儿,别的都没有。大奶奶、二奶奶他两个是每人两匹纱、两匹罗,两个香袋儿,两个锭子药。” \u3000\u3000宝玉听了,笑道:“这是怎么个原故,怎么林姑娘的倒不和我的一样,倒是宝姐姐的和我一样?别是传错了罢?”袭人道:“昨儿拿出来,都是一分一分的写着签子,怎么会错了呢。你的是在老太太屋里,我去拿了来了的。老太太说了:明儿叫你一个五更天进去谢恩呢。”宝玉道:“自然要走一趟。”说着,便叫了紫鹃来:“拿了这个到你们姑娘那里去,就说是昨儿我得的,爱什么留下什么。”紫鹃答应了,拿了去。不一时回来,说:“姑娘说了,昨儿也得了,二爷留着罢。”宝玉听说,便命人收了。', doc_id='f2de2507-9196-462c-98e1-29a3099d0948', extra_info=None, node_info={'start': 180390, 'end': 181896}, similarity=0.844365521297281, image=None)], extra_info=None)

结果不太理想,source_nodes 数据太少,我们换一个问题:

index.query("贾宝玉和袭人是什么关系?")
执行日志

INFO:llama_index.token_counter.token_counter:> [query] Total LLM token usage: 3586 tokens
INFO:llama_index.token_counter.token_counter:> [query] Total embedding token usage: 26 tokens
Response(response='\n贾宝玉和袭人是家庭关系,袭人是宝玉的叔叔。', source_nodes=[SourceNode(source_text='\u3000\u3000袭人、麝月、秋纹三个人正和宝玉玩笑呢,见他两个来了,都忙起来笑道:“你们两个来的怎么碰巧,一齐来了。”一面说,一面接过来。玉钏儿便向一张杌子上坐下,莺儿不敢坐,袭人便忙端了个脚踏来,莺儿还不敢坐。宝玉见莺儿来了,却倒十分欢喜。见了玉钏儿,便想起他姐姐金钏儿来了,又是伤心,又是惭愧,便把莺儿丢下,且和玉钏儿说话。袭人见把莺儿不理,恐莺儿没好意思的,又见莺儿不肯坐,便拉了莺儿出来,到那边屋里去吃茶说话儿去了。 \u3000\u3000这里麝月等预备了碗箸来伺候吃饭。宝玉只是不吃,问玉钏儿道:“你母亲身上好?”玉钏儿满脸娇嗔,正眼也不看宝玉,半日方说了一个“好”字。宝玉便觉没趣,半日,只得又陪笑问道:“谁叫你替我送来的?”玉钏儿道:“不过是奶奶太太们!”宝玉见他还是哭丧着脸,便知他是为金钏儿的原故。待要虚心下气哄他,又见人多,不好下气的,因而便寻方法将人都支出去,然后又陪笑问长问短。那玉钏儿先虽不欲理他,只管见宝玉一些性气也没有,凭他怎么丧谤,还是温存和气,自己倒不好意思的了,脸上方有三分喜色。宝玉便笑央道:“好姐姐,你把那汤端了来,我尝尝。”玉钏儿道:“我从不会喂人东西,等他们来了再喝。”宝玉笑道:“我不是要你喂我,我因为走不动,你递给我喝了,你好赶早回去交代了,好吃饭去。我只管耽误了时候,岂不饿坏了你。你要懒怠动,我少不得忍着疼下去取去。”说着,便要下床,扎挣起来,禁不住“嗳哟”之声。玉钏儿见他这般,也忍不过,起身说道:“躺下去罢!那世里造的孽,这会子现世现报,叫我那一个眼睛瞧的上!”一面说,一面哧的一声又笑了,端过汤来。宝玉笑道:“好姐姐你要生气,只管在这里生罢,见了老太太、太太,可和气着些。若还这样,你就要挨骂了。”玉钏儿道:“吃罢,吃罢!你不用和我甜嘴蜜舌的了,我都知道啊!”说着,催宝玉喝了两口汤。宝玉故意说不好吃。玉钏儿撇嘴道:“阿弥陀佛!这个还不好吃,也不知什么好吃呢!”宝玉道:“一点味儿也没有,你不信尝一尝就知道了。”玉钏儿果真赌气尝了一尝。宝玉笑道:“这可好吃了!”玉钏儿听说,方解过他的意思来,原是宝玉哄他喝一口,便说道:“你即说不喝,这会子说好吃,也不给你喝了。”宝玉只管陪笑央求要喝,玉钏儿又不给他,一面又叫人打发吃饭。 \u3000\u3000丫头方进来时,忽有人来回话,说:“傅二爷家的两个嬷嬷来请安,来见二爷。”宝玉听说,便知是通判傅试家的嬷嬷来了。那傅试原是贾政的门生,原来都赖贾家的名声得意,贾政也着实看待,与别的门生不同;他那里常遣人来走动。宝玉素昔最厌勇男蠢妇的,今日却如何又命这两个婆子进来?其中原来有个原故。只因那宝玉闻得傅试有个妹子,名唤傅秋芳,也是个琼闺秀玉,常听人说才貌俱全,虽自未亲睹,然遐思遥爱之心十分诚敬。不命他们进来,恐薄了傅秋芳,因此连忙命让进来。那傅试原是暴发的,因傅秋芳有几分姿色,聪明过人,那傅试安心仗着妹子,要与豪门贵族结亲,不肯轻意许人,所以耽误到如今。目今傅秋芳已二十三岁,尚未许人。怎奈那些豪门贵族又嫌他本是穷酸,根基浅薄,不肯求配。那傅试与贾家亲密,也自有一段心事。 \u3000\u3000今日遣来的两个婆子,偏偏是极无知识的,闻得宝玉要见,进来只刚问了好,说了没两句话。那玉钏儿见生人来,也不和宝玉厮闹了,手里端着汤,却只顾听。宝玉又只顾和婆子说话,一面吃饭,伸手去要汤,两个人的眼睛都看着人,不想伸猛了手,便将碗撞翻,将汤泼了宝玉手上。玉钏儿倒不曾烫着,吓了一跳,忙笑道:“这是怎么了?”慌的丫头们忙上来接碗。宝玉自己烫了手,倒不觉的,只管问玉钏儿:“烫了那里了?疼不疼?”玉钏儿和众人都笑了。玉钏儿道:“你自己烫了,只管问我。”宝玉听了,方觉自己烫了。众人上来,连忙收拾。宝玉也不吃饭了,洗手吃茶,又和那两个婆子说了两句话,然后两个婆子告辞出去。睛雯等送至桥边方回。', doc_id='f2de2507-9196-462c-98e1-29a3099d0948', extra_info=None, node_info={'start': 226949, 'end': 228506}, similarity=0.8527959063305491, image=None)], extra_info=None)

结果依然不太理想,或许是我们的数据太大,或许红楼梦半白半文不容易理解,我们换一个数据集试试。

重启一个 python 项目,新建一个新的 data 文件夹,在第四届百篇优秀裁判文书网站,下载几篇文书,保存到我们的 data 文件夹,重新执行一次上面的代码,从 import os 开始。

也可直接下载我的data.zip

如果你在执行 index = GPTSimpleVectorIndex.from_documents(documents) 时遇到以下错误:

错误日志

log

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/python3.11/site-packages/llama_index/indices/base.py", line 99, in from_documents
    nodes = service_context.node_parser.get_nodes_from_documents(documents)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/llama_index/node_parser/simple.py", line 30, in get_nodes_from_documents
    nodes = get_nodes_from_document(
            ^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/llama_index/node_parser/node_utils.py", line 49, in get_nodes_from_document
    text_splits = get_text_splits_from_document(
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/llama_index/node_parser/node_utils.py", line 30, in get_text_splits_from_document
    text_splits = text_splitter.split_text_with_overlaps(
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/llama_index/langchain_helpers/text_splitter.py", line 157, in split_text_with_overlaps
    raise ValueError(
ValueError: A single term is larger than the allowed chunk size.
Term size: 5132
Chunk size: 3900Effective chunk size: 3900

表示 llmam-index 在处理文本时,文本切片大小太大,可以认为一个有效的切片应小于 4096(Gpt 3.5 的最大 Token 范围),llama-index 默认最大值是 3900。之所以出现这个错误,是因为 llama-index 在处理分割文本时,是以空格作为分割符,而中文内容,一般是以 等作为分割符,所以对于长段落,较容易出现以上错误,这个 Issue:How to fixed "ValueError: A single term is larger than the allowed chunk size."有相关的讨论。

我的做法是把所有的中文逗号句号替换为英文的逗号句号并加空格

如果你是直接下载上面我提供的数据集,那么应该没有这个问题。
如果你是直接下载上面我提供的数据集,那么你也可以直接下载我的 index.json.zip(此结果不可用,后面有记录),无需消耗 Tokens —— 820198 tokens,约 0.33美元

接下来开始保存索引结果到磁盘并从磁盘中加载,然后查询。

index.query("谁是杀人犯?")

然后又出现异常了。

异常日志

log

INFO:openai:error_code=None error_message="This model's maximum context length is 4097 tokens, however you requested 4134 tokens (3878 in your prompt; 256 for the completion). Please reduce your prompt; or completion length." error_param=None error_type=invalid_request_error message='OpenAI API error received' stream_error=False
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/python3.11/site-packages/llama_index/indices/base.py", line 244, in query
    return query_runner.query(query_str)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/llama_index/indices/query/query_runner.py", line 342, in query
    return query_combiner.run(query_bundle, level)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/llama_index/indices/query/query_combiner/base.py", line 66, in run
    return self._query_runner.query_transformed(
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/llama_index/indices/query/query_runner.py", line 202, in query_transformed
    return query_obj.query(query_bundle)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/llama_index/token_counter/token_counter.py", line 78, in wrapped_llm_predict
    f_return_val = f(_self, *args, **kwargs)
                   ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/llama_index/indices/query/base.py", line 394, in query
    return self._query(query_bundle)
           ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/llama_index/indices/query/base.py", line 381, in _query
    return self.synthesize(query_bundle, nodes)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/llama_index/indices/query/base.py", line 338, in synthesize
    response_str = self._give_response_for_nodes(
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/llama_index/indices/query/base.py", line 224, in _give_response_for_nodes
    response = response_builder.get_response(
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/llama_index/indices/response/builder.py", line 380, in get_response
    return self._get_response_default(query_str, prev_response)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/llama_index/indices/response/builder.py", line 240, in _get_response_default
    return self.get_response_over_chunks(
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/llama_index/indices/response/builder.py", line 222, in get_response_over_chunks
    response = self.give_response_single(
               ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/llama_index/indices/response/builder.py", line 178, in give_response_single
    ) = self._service_context.llm_predictor.predict(
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/llama_index/llm_predictor/base.py", line 223, in predict
    llm_prediction = self._predict(prompt, **prompt_args)
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/llama_index/llm_predictor/base.py", line 197, in _predict
    llm_prediction = retry_on_exceptions_with_backoff(
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/llama_index/utils.py", line 162, in retry_on_exceptions_with_backoff
    return lambda_fn()
           ^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/llama_index/llm_predictor/base.py", line 198, in <lambda>
    lambda: llm_chain.predict(**full_prompt_args),
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/langchain/chains/llm.py", line 151, in predict
    return self(kwargs)[self.output_key]
           ^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/langchain/chains/base.py", line 116, in __call__
    raise e
  File "/usr/local/lib/python3.11/site-packages/langchain/chains/base.py", line 113, in __call__
    outputs = self._call(inputs)
              ^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/langchain/chains/llm.py", line 57, in _call
    return self.apply([inputs])[0]
           ^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/langchain/chains/llm.py", line 118, in apply
    response = self.generate(input_list)
               ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/langchain/chains/llm.py", line 62, in generate
    return self.llm.generate_prompt(prompts, stop)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/langchain/llms/base.py", line 107, in generate_prompt
    return self.generate(prompt_strings, stop=stop)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/langchain/llms/base.py", line 140, in generate
    raise e
  File "/usr/local/lib/python3.11/site-packages/langchain/llms/base.py", line 137, in generate
    output = self._generate(prompts, stop=stop)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/langchain/llms/openai.py", line 281, in _generate
    response = completion_with_retry(self, prompt=_prompts, **params)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/langchain/llms/openai.py", line 99, in completion_with_retry
    return _completion_with_retry(**kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/tenacity/__init__.py", line 289, in wrapped_f
    return self(f, *args, **kw)
           ^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/tenacity/__init__.py", line 379, in __call__
    do = self.iter(retry_state=retry_state)
         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/tenacity/__init__.py", line 314, in iter
    return fut.result()
           ^^^^^^^^^^^^
  File "/usr/local/Cellar/python@3.11/3.11.2_1/Frameworks/Python.framework/Versions/3.11/lib/python3.11/concurrent/futures/_base.py", line 449, in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
  File "/usr/local/Cellar/python@3.11/3.11.2_1/Frameworks/Python.framework/Versions/3.11/lib/python3.11/concurrent/futures/_base.py", line 401, in __get_result
    raise self._exception
  File "/usr/local/lib/python3.11/site-packages/tenacity/__init__.py", line 382, in __call__
    result = fn(*args, **kwargs)
             ^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/langchain/llms/openai.py", line 97, in _completion_with_retry
    return llm.client.create(**kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/openai/api_resources/completion.py", line 25, in create
    return super().create(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/openai/api_resources/abstract/engine_api_resource.py", line 153, in create
    response, _, api_key = requestor.request(
                           ^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/openai/api_requestor.py", line 226, in request
    resp, got_stream = self._interpret_response(result, stream)
                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/openai/api_requestor.py", line 619, in _interpret_response
    self._interpret_response_line(
  File "/usr/local/lib/python3.11/site-packages/openai/api_requestor.py", line 682, in _interpret_response_line
    raise self.handle_error_response(
openai.error.InvalidRequestError: This model's maximum context length is 4097 tokens, however you requested 4134 tokens (3878 in your prompt; 256 for the completion). Please reduce your prompt; or completion length.

openai.error.InvalidRequestError: This model's maximum context length is 4097 tokens, however you requested 4134 tokens (3878 in your prompt; 256 for the completion).

使用上面异常信息在 llama-indexIssues上搜索,没有内容,换成 openai.error.InvalidRequestError,找到一个:Chunk size sometimes exceeds max model size,大意还是因为非英文字符导致的,其中Disiok提供了一个解决方法,如下:

service_context = ServiceContext.from_defaults(chunk_size_limit=3000)
index = GPTListIndex.from_documents(documents, service_context)

我们把这段代码插到我们的代码里,重开一个 pyhton3 运行环境,完整代码如下:

这意味着上面的索引结果已不可用。

我们使用的是 GPTSimpleVectorIndex 类,不是上面回答里的 GPTListIndex,请注意。
如果你是直接下载上面我提供的数据集,那么此过程会消耗 827917 tokens,约 0.33美元,你也可以直接下载我获取的索引结果:index.json.zip

完整代码

test.py

import os
os.environ["OPENAI_API_KEY"] = 'YOUR_OPENAI_API_KEY'

from llama_index import GPTSimpleVectorIndex, SimpleDirectoryReader, ServiceContext
documents = SimpleDirectoryReader('data').load_data()
service_context = ServiceContext.from_defaults(chunk_size_limit=3000)
index = GPTSimpleVectorIndex.from_documents(documents = documents, service_context = service_context)

index.save_to_disk('index.json')
index = GPTSimpleVectorIndex.load_from_disk('index.json')

然后我们再次查询谁是杀人犯这个问题。

index.query("谁是杀人犯?")

查询结果如下:

执行日志

INFO:llama_index.token_counter.token_counter:> [query] Total LLM token usage: 3177 tokens
INFO:llama_index.token_counter.token_counter:> [query] Total embedding token usage: 13 tokens
Response(response='\n根据上述信息,杀人犯是黄国雄、张国和、曹佰稳三人。他们三人一起持刀追砍被害人罗某,并且曹佰稳喊砍死他们。黄建华也参与了持刀追砍被害人,但他的行为没有导致被害人死亡,因此他不是杀人犯。', source_nodes=[SourceNode(source_text='主动到公安机关反映盗窃问题, 供述:当时我拦了一部摩托车来到高叉水路口, 看到很多人拿着刀棍在追打一名男子, 当时我就下车在旁边看了一下, 看到那名男子被人砍到在公路边躺着不动了, 那些砍人的人就走了, 我也拦了一部摩托车走了. 砍人的人我只是面熟, 但不知道人名和绰号. 案发后我逃跑是因为我盗窃摩托车. \n2019年8月7日归案后在侦查阶段的供述:2005年的一天晚上, 我开着一辆右肽黑色皇冠小汽车, 车上坐着有黄国雄、张国和、曹佰稳, 没多久黄国雄叫来一个女子, 并把她带在车里坐车的后排, 我们开车带着那个女子在县城转了一圈. 不久我听到黄国雄说那个女子的朋友在大坪那里等我们讲数, 我就开车搭着他们往大坪方向走. 到达大坪村后, 在加油站附近看到有辆摩托车, 黄国雄叫我靠边停车, 黄国雄、张国和、曹佰稳还有车上那个女的一起下车, 我在车上看到他们几个讲的火气很大. 看到骑摩托车的那些人想离开, 黄国雄叫我开车拦住他们, 我听到后就开车追上去逼停那骑摩托车的两个人, 那两个人摔倒在地, 后站起来就往彭寨路口跑, 接着黄国雄、张国和、曹佰稳三个人下车从后尾箱拿刀追他们, 曹佰稳喊砍死他们, 接着我也下车, 下车后看到黄某1、黄某4两个人也赶到了大坪. 黄国雄、张国和、曹佰稳用刀乱砍骑摩托车的那个人, 我听到很恐怖的声音, 黄国雄、张国和、曹佰稳都喊砍死他们, 对方也高喊不要砍. 接着我开黑色右肽皇冠小轿车往黄国雄、张国和、曹佰稳他们砍人的位置去, 他们看到我的车来了, 黄国雄、张国和就把砍人的刀放到车尾箱, 曹佰稳拿着一把长刀上了副驾驶位置, 我叫他把长刀放到后尾箱去, 在车上黄国雄说他受伤出血了, 要去××街道诊所包扎下. 我开车送他们三个到××后就把车开回黄某1住的地方. \n一审庭审供述:我有犯罪, 我用车逼停被害人, 只是踢了被害人几脚, 我承认拿水果刀追被害人, 但我没砍被害人. 我愿意在具结书上签名. \n二审庭审供述:我没有砍被害人, 我是认罪的, 但我没有拿刀, 我不承认一审认定的事实, 不承认公诉机关提出的十年有期徒刑量刑建议, 我只是开车, 踢了被害人一脚, 我是认罪认罚的. \n综合检、辩双方的意见, 归纳出本案争议的焦点, 并评判如下:\n1.关于上诉人黄建华是否拿刀具与同案人一起追砍罗某\n现场目击证人黄某2、黄某1、黄某4作证, 案发时, 黄建华打电话给黄某1, 让黄某2、黄某1、黄某4等人赶去帮忙, 三人来到案发现场, 看到黄建华持刀和他人一起砍被害人;同案人黄国雄、张国和、曹佰稳归案后均指证黄建华参与持刀砍被害人;公安机关起获四把刀具, 鉴定意见证实被害人致死原因系生前被他人持锐器砍击肢体引起大出血而死亡, 上述在案证据相互印证, 足以认定黄建华持刀和同案人一起追砍被害人罗某, 应对罗某的死亡结果承担法律责任. \n2.关于上诉人黄建华在故意伤害共同犯罪中的地位、作用及相关量刑情节\n根据现场目击证人和同案人指证, 结合黄建华本人供述, 可以认定黄建华除和同案人一起持刀追砍罗某, 还有开车搭载三名同案人将伍某强行拽到车上逼迫其交保护费, 打电话给黄某1让其带人赶到现场帮忙, 驾驶车辆碰撞截停已经驾驶摩托车离开的罗某, 砍人后开车搭载同案人逃离现场的行为, 综上, 黄建华在故意伤害共同犯罪中行为积极主动, 在整个犯罪过程中起主要作用, 应认定是主犯;2011年11月14日, 黄建华虽主动到派出所, 但向公安机关坦白的是盗窃摩托车的情况, 在公安讯问故意伤害罗某的情况时,', doc_id='349b0f06-4907-4914-bf13-a5e8b684cf32', extra_info=None, node_info={'start': 7617, 'end': 9086}, similarity=0.817000073502974, image=None)], extra_info=None)

结果还可以,消耗约 3177 LLM tokens 和 13 embedding tokens,约 0.006美元,我们继续追问:

index.query("黄国雄为什么要杀人?")

查询结果如下:

执行日志

INFO:llama_index.token_counter.token_counter:> [query] Total LLM token usage: 3125 tokens
INFO:llama_index.token_counter.token_counter:> [query] Total embedding token usage: 21 tokens
Response(response='\n黄国雄杀人的原因不详,但根据上述信息可以推断,他可能是为了强行拽伍某,并要求其交保护费,或者是为了阻止罗某离开而发动攻击。', source_nodes=[SourceNode(source_text='主动到公安机关反映盗窃问题, 供述:当时我拦了一部摩托车来到高叉水路口, 看到很多人拿着刀棍在追打一名男子, 当时我就下车在旁边看了一下, 看到那名男子被人砍到在公路边躺着不动了, 那些砍人的人就走了, 我也拦了一部摩托车走了. 砍人的人我只是面熟, 但不知道人名和绰号. 案发后我逃跑是因为我盗窃摩托车. \n2019年8月7日归案后在侦查阶段的供述:2005年的一天晚上, 我开着一辆右肽黑色皇冠小汽车, 车上坐着有黄国雄、张国和、曹佰稳, 没多久黄国雄叫来一个女子, 并把她带在车里坐车的后排, 我们开车带着那个女子在县城转了一圈. 不久我听到黄国雄说那个女子的朋友在大坪那里等我们讲数, 我就开车搭着他们往大坪方向走. 到达大坪村后, 在加油站附近看到有辆摩托车, 黄国雄叫我靠边停车, 黄国雄、张国和、曹佰稳还有车上那个女的一起下车, 我在车上看到他们几个讲的火气很大. 看到骑摩托车的那些人想离开, 黄国雄叫我开车拦住他们, 我听到后就开车追上去逼停那骑摩托车的两个人, 那两个人摔倒在地, 后站起来就往彭寨路口跑, 接着黄国雄、张国和、曹佰稳三个人下车从后尾箱拿刀追他们, 曹佰稳喊砍死他们, 接着我也下车, 下车后看到黄某1、黄某4两个人也赶到了大坪. 黄国雄、张国和、曹佰稳用刀乱砍骑摩托车的那个人, 我听到很恐怖的声音, 黄国雄、张国和、曹佰稳都喊砍死他们, 对方也高喊不要砍. 接着我开黑色右肽皇冠小轿车往黄国雄、张国和、曹佰稳他们砍人的位置去, 他们看到我的车来了, 黄国雄、张国和就把砍人的刀放到车尾箱, 曹佰稳拿着一把长刀上了副驾驶位置, 我叫他把长刀放到后尾箱去, 在车上黄国雄说他受伤出血了, 要去××街道诊所包扎下. 我开车送他们三个到××后就把车开回黄某1住的地方. \n一审庭审供述:我有犯罪, 我用车逼停被害人, 只是踢了被害人几脚, 我承认拿水果刀追被害人, 但我没砍被害人. 我愿意在具结书上签名. \n二审庭审供述:我没有砍被害人, 我是认罪的, 但我没有拿刀, 我不承认一审认定的事实, 不承认公诉机关提出的十年有期徒刑量刑建议, 我只是开车, 踢了被害人一脚, 我是认罪认罚的. \n综合检、辩双方的意见, 归纳出本案争议的焦点, 并评判如下:\n1.关于上诉人黄建华是否拿刀具与同案人一起追砍罗某\n现场目击证人黄某2、黄某1、黄某4作证, 案发时, 黄建华打电话给黄某1, 让黄某2、黄某1、黄某4等人赶去帮忙, 三人来到案发现场, 看到黄建华持刀和他人一起砍被害人;同案人黄国雄、张国和、曹佰稳归案后均指证黄建华参与持刀砍被害人;公安机关起获四把刀具, 鉴定意见证实被害人致死原因系生前被他人持锐器砍击肢体引起大出血而死亡, 上述在案证据相互印证, 足以认定黄建华持刀和同案人一起追砍被害人罗某, 应对罗某的死亡结果承担法律责任. \n2.关于上诉人黄建华在故意伤害共同犯罪中的地位、作用及相关量刑情节\n根据现场目击证人和同案人指证, 结合黄建华本人供述, 可以认定黄建华除和同案人一起持刀追砍罗某, 还有开车搭载三名同案人将伍某强行拽到车上逼迫其交保护费, 打电话给黄某1让其带人赶到现场帮忙, 驾驶车辆碰撞截停已经驾驶摩托车离开的罗某, 砍人后开车搭载同案人逃离现场的行为, 综上, 黄建华在故意伤害共同犯罪中行为积极主动, 在整个犯罪过程中起主要作用, 应认定是主犯;2011年11月14日, 黄建华虽主动到派出所, 但向公安机关坦白的是盗窃摩托车的情况, 在公安讯问故意伤害罗某的情况时,', doc_id='349b0f06-4907-4914-bf13-a5e8b684cf32', extra_info=None, node_info={'start': 7617, 'end': 9086}, similarity=0.8485231373129285, image=None)], extra_info=None)

结果基本符合《(2020)粤刑终986号》判决书的结论,但 source_nodes 的内容,依然不够清晰,或许需要我们进一步处理数据,也可能是上面的代码太粗糙了。

OK,到这里就结束了,如果你有疑问,欢迎留言评论。

TOP
前往 GitHub Discussion 评论