当前位置:   article > 正文

feature1 书生·浦语大模型全链路开源开放体系 学习心得

feature1 书生·浦语大模型全链路开源开放体系 学习心得

课程视频链接:书生·浦语大模型全链路开源体系_哔哩哔哩_bilibili

大模型的趋势是从专用模型(不同方向细分模型)转变为通用大模型(一个模型可以应对多种任务或者多种模态)。书生·浦语大模型(InternLM2)是当前优秀的开源通用大语言模型(LLM),其也提供了7B和20B的InternLM、InternLM-Chat、InternLM-Base、InternLM-Math、InternLM-XComposer供我们调用。

我个人的兴趣点在于大模型的应用,在课程中了解到从模型到应用的一般流程如下(图片来源课程视频):

图中,我特别关注了构建智能体模块。大模型聚焦于推理,其在环境的交互能力上功能欠缺,这里的交互指的是:例如,你询问LLM今天的天气并希望LLM根据今天的气温提供出行建议,当前的LLM并不能给出实时的建议。

为了解决实时性交互的问题(即LLM通过推理结果执行Action),智能体(Agent)的概念便被提出了。Agent的核心在于Action的执行,视频中提到了Lagent轻量级智能体框架,这里也简单阅读了一下源码。

在分析Lagent源码之前,可以先看datawhalechina开源的一个仓库: tiny-universe ,其中的TinyAgent项目构建了一个迷你Agent,学习这个项目有助于理解Lagent。TinyAgent遵循React模式,通过:Thought/Action/Action Input/Observation 不断循环迭代的方式交替生成推理轨迹和执行特定任务,使得LLM能够更好的完成实时交互任务。下面给出了项目中Agent.py的源码,可以看到:

  • Agent的初始化阶段主要负责加载 ReAct Prompt、加载 LLM Model、加载 tools。
  • Agent的text_completion阶段涉及到两次 LLM Model 的调用。第一次是调用 LLM 获取Action需要用到的 plugin_name 以及 plugin_args,而后Agent调用Plugin的call方法得到obsercation并加入到对话中。第二次调用是 LLM 通过前面的得到的对话进一步得到 Tought 和 Final Answer。
  1. TOOL_DESC = """{name_for_model}: Call this tool to interact with the {name_for_human} API. What is the {name_for_human} API useful for? {description_for_model} Parameters: {parameters} Format the arguments as a JSON object."""
  2. REACT_PROMPT = """Answer the following questions as best you can. You have access to the following tools:
  3. {tool_descs}
  4. Use the following format:
  5. Question: the input question you must answer
  6. Thought: you should always think about what to do
  7. Action: the action to take, should be one of [{tool_names}]
  8. Action Input: the input to the action
  9. Observation: the result of the action
  10. ... (this Thought/Action/Action Input/Observation can be repeated zero or more times)
  11. Thought: I now know the final answer
  12. Final Answer: the final answer to the original input question
  13. Begin!
  14. """
  15. class Agent:
  16. def __init__(self, path: str = '') -> None:
  17. self.path = path
  18. self.tool = Tools()
  19. self.system_prompt = self.build_system_input()
  20. self.model = InternLM2Chat(path)
  21. def build_system_input(self):
  22. tool_descs, tool_names = [], []
  23. for tool in self.tool.toolConfig:
  24. tool_descs.append(TOOL_DESC.format(**tool))
  25. tool_names.append(tool['name_for_model'])
  26. tool_descs = '\n\n'.join(tool_descs)
  27. tool_names = ','.join(tool_names)
  28. sys_prompt = REACT_PROMPT.format(tool_descs=tool_descs, tool_names=tool_names)
  29. return sys_prompt
  30. def parse_latest_plugin_call(self, text):
  31. plugin_name, plugin_args = '', ''
  32. i = text.rfind('\nAction:')
  33. j = text.rfind('\nAction Input:')
  34. k = text.rfind('\nObservation:')
  35. if 0 <= i < j: # If the text has `Action` and `Action input`,
  36. if k < j: # but does not contain `Observation`,
  37. text = text.rstrip() + '\nObservation:' # Add it back.
  38. k = text.rfind('\nObservation:')
  39. plugin_name = text[i + len('\nAction:') : j].strip()
  40. plugin_args = text[j + len('\nAction Input:') : k].strip()
  41. text = text[:k]
  42. return plugin_name, plugin_args, text
  43. def call_plugin(self, plugin_name, plugin_args):
  44. plugin_args = json5.loads(plugin_args)
  45. if plugin_name == 'google_search':
  46. return '\nObservation:' + self.tool.google_search(**plugin_args)
  47. def text_completion(self, text, history=[]):
  48. text = "\nQuestion:" + text
  49. response, his = self.model.chat(text, history, self.system_prompt)
  50. print(response)
  51. plugin_name, plugin_args, response = self.parse_latest_plugin_call(response)
  52. if plugin_name:
  53. response += self.call_plugin(plugin_name, plugin_args)
  54. response, his = self.model.chat(response, history, self.system_prompt)
  55. return response, his

从上述流程可以看出,TinyAgent似乎只能进行一次工具调用,这也让我对Lagent是如何实现Thought/Action/Action Input/Observation的循环迭代产生好奇。直接打开Lagent的开源仓库,从源码目录结构可以看出代码核心是:actions、agents和llms,这里也简单对相关代码进行分析。

1. BaseModel类(llms目录下)

该框架兼容OpenAI API、Transformer、LMDeploy。因此这里先简单分析一下BaseModel类,GPTAPI、HFTransformer、LMDeployPipeline等类都继承了BaseModel,并重写或者补充了一些方法。

  1. class BaseModel:
  2. is_api: bool = False
  3. def __init__(self,
  4. path: str,
  5. tokenizer_only: bool = False,
  6. template_parser: 'LMTemplateParser' = LMTemplateParser,
  7. meta_template: Optional[List[Dict]] = None,
  8. *,
  9. max_new_tokens: int = 512,
  10. top_p: float = 0.8,
  11. top_k: float = 40,
  12. temperature: float = 0.8,
  13. repetition_penalty: float = 1.0,
  14. stop_words: Union[List[str], str] = None):
  15. ...
  16. def generate(self, inputs: Union[str, List[str]], **gen_params) -> str:
  17. ...
  18. def stream_generate(self, inputs: str, **gen_params) -> List[str]:
  19. ...
  20. def chat(self, inputs: Union[List[dict], List[List[dict]]], **gen_params):
  21. ...
  22. def generate_from_template(self, inputs: Union[List[dict],
  23. List[List[dict]]],
  24. **gen_params):
  25. ...
  26. def stream_chat(self, inputs: List[dict], **gen_params):
  27. ...
  28. def tokenize(self, prompts: Union[str, List[str], List[dict],
  29. List[List[dict]]]):
  30. ...
  31. def update_gen_params(self, **kwargs):
  32. ...

2. BaseAction类(actions目录下)

BaseAction定义了所有actions的基类并给出了自定义action的方法。

  1. class BaseAction(metaclass=AutoRegister(TOOL_REGISTRY, ToolMeta)):
  2. """Base class for all actions.
  3. Args:
  4. description (:class:`Optional[dict]`): The description of the action.
  5. Defaults to ``None``.
  6. parser (:class:`Type[BaseParser]`): The parser class to process the
  7. action's inputs and outputs. Defaults to :class:`JsonParser`.
  8. enable (:class:`bool`): Whether the action is enabled. Defaults to
  9. ``True``.
  10. Examples:
  11. * simple tool
  12. .. code-block:: python
  13. class Bold(BaseAction):
  14. '''Make text bold'''
  15. def run(self, text: str):
  16. '''
  17. Args:
  18. text (str): input text
  19. Returns:
  20. str: bold text
  21. '''
  22. return '**' + text + '**'
  23. action = Bold()
  24. * toolkit with multiple APIs
  25. .. code-block:: python
  26. class Calculator(BaseAction):
  27. '''Calculator'''
  28. @tool_api
  29. def add(self, a, b):
  30. '''Add operation
  31. Args:
  32. a (int): augend
  33. b (int): addend
  34. Returns:
  35. int: sum
  36. '''
  37. return a + b
  38. @tool_api
  39. def sub(self, a, b):
  40. '''Subtraction operation
  41. Args:
  42. a (int): minuend
  43. b (int): subtrahend
  44. Returns:
  45. int: difference
  46. '''
  47. return a - b
  48. action = Calculator()
  49. """
  50. ...

3. react.py(agents目录下)

ReAct继承自BaseAgent,其流程其实和TinyAgent差不多。为了实现可以不断调用工具并在适当调用次数下得到Final Answer,这里的做法是:

  • 做法一:设置一个 max_turn,一旦到达最大次数就输出Final Answer
  • 做法二:在Prompt中提到LLM一旦认为可以结束tools调用了就会输出{thought}和{finish}。LLM通过自主判断是否输出"final answer",一旦出现“final answer”,ReActProtocol中的Parse函数便会返回设定 action_return.type == self._action_executor.finish_action.name,于是函数调用停止。

    If you already know the answer, or you do not need to use tools,
    please using the following format to reply:
    ```
    {thought}the thought process to get the final answer
    {finish}final answer
    ```

  1. CALL_PROTOCOL_EN = """You are a assistant who can utilize external tools.
  2. {tool_description}
  3. To use a tool, please use the following format:
  4. ```
  5. {thought}Think what you need to solve, do you need to use tools?
  6. {action}the tool name, should be one of [{action_names}]
  7. {action_input}the input to the action
  8. ```
  9. The response after utilizing tools should using the following format:
  10. ```
  11. {response}the results after call the tool.
  12. ```
  13. If you already know the answer, or you do not need to use tools,
  14. please using the following format to reply:
  15. ```
  16. {thought}the thought process to get the final answer
  17. {finish}final answer
  18. ```
  19. Begin!"""
  1. class ReAct(BaseAgent):
  2. """An implementation of ReAct (https://arxiv.org/abs/2210.03629)
  3. Args:
  4. llm (BaseModel or BaseAPIModel): a LLM service which can chat
  5. and act as backend.
  6. action_executor (ActionExecutor): an action executor to manage
  7. all actions and their response.
  8. protocol (ReActProtocol): a wrapper to generate prompt and
  9. parse the response from LLM / actions.
  10. max_turn (int): the maximum number of trails for LLM to generate
  11. plans that can be successfully parsed by ReAct protocol.
  12. Defaults to 4.
  13. """
  14. def __init__(self,
  15. llm: Union[BaseModel, BaseAPIModel],
  16. action_executor: ActionExecutor,
  17. protocol: ReActProtocol = ReActProtocol(),
  18. max_turn: int = 4) -> None:
  19. self.max_turn = max_turn
  20. super().__init__(
  21. llm=llm, action_executor=action_executor, protocol=protocol)
  22. def chat(self, message: Union[str, dict, List[dict]],
  23. **kwargs) -> AgentReturn:
  24. if isinstance(message, str):
  25. inner_history = [dict(role='user', content=message)]
  26. elif isinstance(message, dict):
  27. inner_history = [message]
  28. elif isinstance(message, list):
  29. inner_history = message[:]
  30. else:
  31. raise TypeError(f'unsupported type: {type(message)}')
  32. offset = len(inner_history)
  33. agent_return = AgentReturn()
  34. default_response = 'Sorry that I cannot answer your question.'
  35. for turn in range(self.max_turn):
  36. prompt = self._protocol.format(
  37. chat_history=[],
  38. inner_step=inner_history,
  39. action_executor=self._action_executor,
  40. force_stop=(turn == self.max_turn - 1))
  41. response = self._llm.chat(prompt, **kwargs)
  42. inner_history.append(dict(role='assistant', content=response))
  43. thought, action, action_input = self._protocol.parse(
  44. response, self._action_executor)
  45. action_return: ActionReturn = self._action_executor(
  46. action, action_input)
  47. action_return.thought = thought
  48. agent_return.actions.append(action_return)
  49. if action_return.type == self._action_executor.finish_action.name:
  50. agent_return.response = action_return.format_result()
  51. break
  52. inner_history.append(self._protocol.format_response(action_return))
  53. else:
  54. agent_return.response = default_response
  55. agent_return.inner_steps = inner_history[offset:]
  56. return agent_return

总结:第一节课我主要关注了从模型到应用的流程,重点了解了智能体的构建。从tinyAgent开源项目入手,结合Lagent源码简单知悉了ReAct模式下模型调用工具的流程。

声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号