使用 InternLM 构建个人 AI 助手:Prompt、RAG、Agent 与 XTuner 微调

3335 字
17 分钟
使用 InternLM 构建个人 AI 助手:Prompt、RAG、Agent 与 XTuner 微调

这篇文章把四次相对独立的实践整理成一条完整路线:先用结构化 Prompt 约束模型,再用 RAG 补充外部知识,随后通过 Agent 连接工具,最后用微调固化稳定的行为与风格。

这四种技术并不是互相替代的关系。它们分别解决“如何表达任务”“如何获得知识”“如何执行动作”和“如何形成长期能力”四类问题。

能力路线#

层次技术主要解决的问题适合优先使用的场景
任务表达LangGPT / Prompt输出不稳定、格式不统一、推理步骤不清晰需求可以通过文字明确描述
外部知识RAG / LlamaIndex模型不知道私有资料或最新内容文档问答、知识库检索
工具执行Lagent模型只能回答,不能搜索、计算或调用服务需要调用工具完成多步任务
行为固化XTuner仅靠提示词难以长期保持风格或能力有高质量训练数据且需求稳定

实际开发时,通常应该按表格从上到下逐层增加复杂度,而不是一开始就进行微调。

LangGPT:先让模型准确理解任务#

结构化提示词是成本最低、反馈最快的一层。下面保留当时针对浮点数比较问题的实践记录,并包含模型服务和 tmux 的基础操作。

前言#

在日常使用大模型时,我发现它经常在数字比对这类基础问题上出错,并且输出结果很不严谨。为了解决这个问题,我尝试使用Prompt Engineering, 并在网上找到了一个开源的「结构化提示词框架」— LangGPT,以下是我的使用过程记录。


实现步骤#

step0:前期准备

  1. 创建虚拟环境->激活虚拟环境->安装必要包文件
  2. 创建项目路径->进入项目
  3. 安装必要软件,如tmux

step1:模型部署 模型下载->部署模型为OpenAI server->图形化界面调用

step3:langgpt结构化提示词编写 偷懒大法:GPTS有LangGPT提示词专家,用大模型生成即可


tmux使用#

tmux可以在终端中创建终端,将进程维持在后台。

所以当我们下载模型,使用tmux在后台下载时,即便我们断开ssh连接,下载也不会中断。

step1:部署模型为OpenAI server

tmux常见命令#

1.创建窗口命令

PS:t表示target(目标),用于指定会话、窗口或面板的名称

Terminal window
tmux new -t <session_name>

PS:创建完成后,运行下面的命令进入新的命令窗口(首次创建自动进入,之后需要连接): 其中aattach 的简写

Terminal window
tmux a -t <session_name>

2.查看当前 tmux 会话

Terminal window
tmux ls

3.连接到特定的 tmux 会话

Terminal window
tmux attach -t <session_name>

<session_name> 替换为你要连接的会话的实际名称或编号。

4.杀死整个 tmux 会话:

Terminal window
tmux kill-session -t <session_name>

5.退出tmux:

Ctrl+B进入tmux的控制模式,然后按d退出窗口连接

模型部署#

进入命令窗口后,需要在新窗口中再次激活环境,命令参考0.1节。然后,使用LMDeploy进行部署,参考如下命令:

使用LMDeploy进行部署,参考如下命令:

Terminal window
CUDA_VISIBLE_DEVICES=0 lmdeploy serve api_server /share/new_models/Shanghai_AI_Laboratory/internlm2-chat-1_8b --server-port 23333 --api-keys internlm2

更多设置参考:https://lmdeploy.readthedocs.io/en/latest/index.html

部署成功后,可以利用如下脚本调用部署的InternLM2-chat-1_8b模型并测试是否部署成功。

from openai import OpenAI
client = OpenAI(
api_key = "internlm2",
base_url = "http://0.0.0.0:23333/v1"
)
response = client.chat.completions.create(
model=client.models.list().data[0].id,
messages=[
{"role": "system", "content": "请介绍一下你自己"}
]
)
print(response.choices[0].message.content)

服务启动完成后,可以按Ctrl+B进入tmux的控制模式,然后按D退出窗口连接,更多操作参考


作业:#

基础任务 (完成此任务即完成闯关):

  • 背景问题:近期相关研究发现,LLM在对比浮点数字时表现不佳,经验证,internlm2-chat-1.8b (internlm2-chat-7b)也存在这一问题,例如认为13.8<13.11

  • 任务要求:利用LangGPT优化提示词,使LLM输出正确结果。完成一次并提交截图即可

使用GPTS中LangGPT提示词专家,配合我们的需求生成LangGPT结构化提示词

生成结果如下

# Role: 数学助手
## Profile
- author: LangGPT
- version: 1.0
- language: 中文
- description: 一个能够进行基本数学加减法运算,并能够比较两个数字大小的助手。
## Skills
1. 能够进行基本的数学加法和减法运算。
2. 能够比较两个数字的大小,并给出相应的判断。
3. 能够理解用户输入的简单数学表达式或问题描述。
## Rules
1. 用户输入可以是任意两个数字或一个包含加法、减法的表达式。
2. 当输入两个数字时,助手应比较它们的大小,并返回结果。
3. 当输入一个加法或减法表达式时,助手应计算出结果并返回。
4. 如果用户输入有误或不符合数学表达式规则,助手应给出提示并要求重新输入。
## Workflows
1. 接收用户输入的数字或数学表达式。
2. 判断输入的类型(两个数字或数学表达式)。
3. 如果是两个数字,执行大小比较,并返回结果。
4. 如果是数学表达式,进行加法或减法计算,返回结果。
5. 如果输入不符合预期格式,返回错误提示,要求用户重新输入。
## Init
1. 向用户介绍助手的功能和使用方法。
2. 提示用户可以输入两个数字进行比较,或输入一个加法、减法表达式进行计算。

加入系统提示词前: PS:估计InternLM2-chat-1_8b版本太久远了,所以回答不出来🤔

加入系统提示词后: PS:效果明显变好了😋

Reference:#

1.LangGPT社区:LangGPT结构化提示词 - 飞书云文档 2.浦语开源文档书生浦语-浦语提示词工程实践 3.文档:系统论述文章: 构建高性能 Prompt 之路——结构化 Prompt

RAG:给助手接入自己的知识库#

当问题来自私有文档或模型训练后的新知识时,仅修改 Prompt 不够。RAG 会先检索相关资料,再把检索结果交给模型生成答案。

一、前置知识#

  • 给模型注入新知识的方式
    • 内部方式:更新模型的权重,但训练代价较大。
    • 外部方式:给模型注入额外的上下文或外部信息,不改变其权重。
  • RAG 工作原理
    • 将问题编码成向量,在向量数据库中找到最相关的文档块(top-k chunks)。
    • 将知识源分割成小块,编码成向量并存储在向量数据库中。
    • 将检索到的文档块与原始问题一起作为提示输入到 LLM 中,生成最终的回答。
  • RAG 效果比对
    • 由于 xtuner 是较新的框架,InternLM2-Chat-1.8B 训练数据库中未收录相关信息,使用 RAG 前问答均未给出准确答案,使用后能获得想要的答案。

二、环境、模型准备#

(一)配置基础环境#

  • Intern Studio 服务器上部署 LlamaIndex

    • 打开 Intern Studio 界面,点击 创建开发机 配置开发机系统。
    • 填写 开发机名称 后,点击 选择镜像 使用 Cuda11.7-conda 镜像,在资源配置中选择 30% A100 * 1 的选项,立即创建开发机器。
    • 进入开发机后,创建新的 conda 环境,命名为 llamaindex,运行以下命令:
    Terminal window
    conda create -n llamaindex python=3.10
    conda env list
    conda activate llamaindex
    conda install pytorch**2.0.1 torchvision**0.15.2 torchaudio==2.0.2 pytorch-cuda=11.7 -c pytorch -c nvidia
    pip install einops**0.7.0 protobuf**5.26.1
    • 环境激活后,命令行左边会显示当前环境名称。

(二)安装 LlamaIndex#

  • 安装 LlamaIndex 和相关的包:

    Terminal window
    conda activate llamaindex
    pip install llama-index**0.10.38 llama-index-llms-huggingface**0.2.0 "transformers[torch]**4.41.1" "huggingface_hub[inference]**0.23.1" huggingface_hub**0.23.1 sentence-transformers**2.7.0 sentencepiece==0.2.0

(三)下载 Sentence Transformer 词嵌入模型#

  • 新建一个 python 文件,贴入以下代码:

    import os
    # 设置环境变量
    os.environ['HF_ENDPOINT'] = 'https://hf-mirror.com'
    # 下载模型
    os.system('huggingface-cli download --resume-download sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2 --local-dir /root/model/sentence-transformer')
  • /root/llamaindex_demo 目录下执行该脚本自动开始下载。

(四)下载 NLTK 相关资源#

  • 使用以下命令下载 nltk 资源并解压到服务器上:

    Terminal window
    cd /root
    git clone https://gitee.com/yzy0612/nltk_data.git --branch gh-pages
    cd nltk_data
    mv packages/* ./
    cd tokenizers
    unzip punkt.zip
    cd ../taggers
    unzip averaged_perceptron_tagger.zip

三、LlamaIndex HuggingFaceLLM#

  • 运行以下指令,把 InternLM2 1.8B 软连接出来:

    Terminal window
    cd ~/model
    ln -s /root/share/new_models/Shanghai_AI_Laboratory/internlm2-chat-1_8b/ ./
  • 新建一个 python 文件,贴入以下代码:

    from llama_index.llms.huggingface import HuggingFaceLLM
    from llama_index.core.llms import ChatMessage
    llm = HuggingFaceLLM(
    model_name="/root/model/internlm2-chat-1_8b",
    tokenizer_name="/root/model/internlm2-chat-1_8b",
    model_kwargs={"trust_remote_code": True},
    tokenizer_kwargs={"trust_remote_code": True}
    )
    rsp = llm.chat(messages=[ChatMessage(content="xtuner 是什么?")])
    print(rsp)
  • 运行以下命令:

    Terminal window
    conda activate llamaindex
    cd ~/llamaindex_demo/
    python llamaindex_internlm.py
  • 结果回答效果并不好,不是想要的 xtuner

四、LlamaIndex RAG#

  • 安装 LlamaIndex 词嵌入向量依赖:

    Terminal window
    conda activate llamaindex
    pip install llama-index-embeddings-huggingface**0.2.0 llama-index-embeddings-instructor**0.1.3
  • 获取知识库:

    Terminal window
    cd ~/llamaindex_demo
    mkdir data
    cd data
    git clone https://github.com/InternLM/xtuner.git
    mv xtuner/README_zh-CN.md ./
  • 新建一个 python 文件,贴入以下代码:

    from llama_index.core import VectorStoreIndex, SimpleDirectoryReader, Settings
    from llama_index.embeddings.huggingface import HuggingFaceEmbedding
    from llama_index.llms.huggingface import HuggingFaceLLM
    # 初始化一个 HuggingFaceEmbedding 对象,用于将文本转换为向量表示
    embed_model = HuggingFaceEmbedding(
    model_name="/root/model/sentence-transformer"
    )
    Settings.embed_model = embed_model
    llm = HuggingFaceLLM(
    model_name="/root/model/internlm2-chat-1_8b",
    tokenizer_name="/root/model/internlm2-chat-1_8b",
    model_kwargs={"trust_remote_code": True},
    tokenizer_kwargs={"trust_remote_code": True}
    )
    Settings.llm = llm
    # 从指定目录读取所有文档,并加载数据到内存中
    documents = SimpleDirectoryReader("/root/llamaindex_demo/data").load_data()
    index = VectorStoreIndex.from_documents(documents)
    query_engine = index.as_query_engine()
    response = query_engine.query("xtuner 是什么?")
    print(response)
  • 运行以下命令:

    Terminal window
    conda activate llamaindex
    cd ~/llamaindex_demo/
    python llamaindex_RAG.py
  • 结果借助 RAG 技术后,能获得想要的答案。

五、LlamaIndex Web#

  • 安装依赖:

    Terminal window
    pip install streamlit==1.36.0
  • 新建一个 python 文件,贴入以下代码:

    import streamlit as st
    from llama_index.core import VectorStoreIndex, SimpleDirectoryReader, Settings
    from llama_index.embeddings.huggingface import HuggingFaceEmbedding
    from llama_index.llms.huggingface import HuggingFaceLLM
    st.set_page_config(page_title="llama_index_demo", page_icon="🦜🔗")
    st.title("llama_index_demo")
    # 初始化模型
    @st.cache_resource
    def init_models():
    embed_model = HuggingFaceEmbedding(
    model_name="/root/model/sentence-transformer"
    )
    Settings.embed_model = embed_model
    llm = HuggingFaceLLM(
    model_name="/root/model/internlm2-chat-1_8b",
    tokenizer_name="/root/model/internlm2-chat-1_8b",
    model_kwargs={"trust_remote_code": True},
    tokenizer_kwargs={"trust_remote_code": True}
    )
    Settings.llm = llm
    documents = SimpleDirectoryReader("/root/llamaindex_demo/data").load_data()
    index = VectorStoreIndex.from_documents(documents)
    query_engine = index.as_query_engine()
    return query_engine
    # 检查是否需要初始化模型
    if 'query_engine' not in st.session_state:
    st.session_state['query_engine'] = init_models()
    def greet2(question):
    response = st.session_state['query_engine'].query(question)
    return response
    # Store LLM generated responses
    if "messages" not in st.session_state.keys():
    st.session_state.messages = [{"role": "assistant", "content": "你好,我是你的助手,有什么我可以帮助你的吗?"}]
    # Display or clear chat messages
    for message in st.session_state.messages:
    with st.chat_message(message["role"]):
    st.write(message["content"])
    def clear_chat_history():
    st.session_state.messages = [{"role": "assistant", "content": "你好,我是你的助手,有什么我可以帮助你的吗?"}]
    st.sidebar.button('Clear Chat History', on_click=clear_chat_history)
    # Function for generating LLaMA2 response
    def generate_llama_response(question):
    response = greet2(question)
    return response
    # User-provided prompt
    if prompt := st.chat_input("What's on your mind?"):
    st.chat_message("user").write(prompt)
    with st.chat_message("assistant"):
    message_placeholder = st.empty()
    response = generate_llama_response(prompt)
    message_placeholder.write(response)
    st.session_state.messages.append({"role": "assistant", "content": response})
  • 启动 Streamlit 服务:

    Terminal window
    cd ~/llamaindex_demo/
    streamlit run llamaindex_streamlit.py
  • 打开浏览器,访问 https://localhost:XXX

六、总结#

  • 本文介绍如何结合 LlamaIndexInternLM2 部署 RAG,为大模型注入最新知识库,效果显著。

七、作业#

RAG前

RAG后

使用Streamlit部署webui(使用RAG)

Lagent:让助手能够调用工具#

RAG 解决“知道什么”,Agent 更关注“能够做什么”。这一部分记录 Lagent WebUI、模型服务、依赖问题和自定义工具的接入过程。

启动webui服务#

使用lmdeploy启动一个api_server

Terminal window
conda activate agent_camp3
lmdeploy serve api_server /share/new_models/Shanghai_AI_Laboratory/internlm2_5-7b-chat --model-name internlm2_5-7b-chat

另开一个终端使用stremlit启动agent_web应用

Terminal window
cd /root/agent_camp3/lagent
conda activate agent_camp3
streamlit run examples/internlm2_agent_web_demo.py

本地powershell建立ssh连接,进行端口映射

Q&A#

遇到的问题:#

ModuleNotFoundError: No module named 'griffe.enumerations

解决方法:#

Due to griffe's recent 1.x release, the `griffe.enumerations` module has been removed, resulting in a break change, which can be resolved by
`pip install griffe==0.48`.

修改模型名称以及模型IP地址#

启动streamlit的web应用如下

修改红框内的内容

终端的反馈

PS:很是可惜,没有找到我想要的内容[1706.03762] Attention Is All You Need (arxiv.org)

自定义工具#

进入lagent_web_demo的py文件中

添加&修改代码:

from lagent.actions import ActionExecutor, ArxivSearch, IPythonInterpreter
+ from lagent.actions.magicmaker import MagicMaker
from lagent.agents.internlm2_agent import INTERPRETER_CN, META_CN, PLUGIN_CN, Internlm2Agent, Internlm2Protocol
...
action_list = [
ArxivSearch(),
+ MagicMaker(),
]

重新启动agent_web服务

使用MagicMaker工具绘画

Prompt:帮我画一个微笑的女孩,穿着校服在学校的走廊上,阳光照耀在她身上

可以看到对于用户输入的提示词,有自动进行填充完善

XTuner:把稳定需求固化到模型中#

微调适合处理长期、重复且有训练样本支持的能力或风格。它不应替代知识库和工具系统;知识经常变化时优先使用 RAG,需要执行动作时仍要使用 Agent。原始记录主要保存了任务要求与结果截图,因此这里不补写未经验证的训练参数。

任务内容#

完成任务截图#

四种方案如何组合#

一个实用的个人助手可以采用这样的工作流:用户输入先经过结构化 Prompt 整理;系统判断是否需要检索本地知识库;如果任务要求搜索、绘图或运行代码,再交给 Agent 调用工具;模型本身则通过少量高质量微调数据保持稳定的人设和表达习惯。

实践建议#

  1. 先用 Prompt 做最小可行版本,确认任务边界。
  2. 知识变化频繁时使用 RAG,不要反复微调事实数据。
  3. 工具权限应尽量最小化,并为调用结果增加校验。
  4. 只有当需求稳定、样本可靠、收益明确时再进行微调。
  5. 用固定测试集分别验证准确率、引用质量、工具成功率和风格一致性。

从 Prompt 到 RAG、Agent、微调,本质上是在逐步扩展助手的可控性、知识范围、行动能力和个性化程度。

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

使用 InternLM 构建个人 AI 助手:Prompt、RAG、Agent 与 XTuner 微调
https://hp-patience.github.io/posts/internlm-personal-ai-assistant/
作者
Celyn
发布于
2024-09-03
许可协议
CC BY-NC-SA 4.0

评论区

Profile Image of the Author
Profile Image of the Author
Celyn
记录 AI、前后端与编程技术学习,用费曼学习法把知识讲清楚。
公告
欢迎来到 Celyn 的博客!专注于AI大模型、深度学习与编程技术分享。
音乐
封面

音乐

暂未播放

0:00 0:00
暂无歌词
分类
标签
站点统计
文章
20
分类
13
标签
53
总字数
53,589
运行时长
0
最后活动
0 天前

文章目录