代理是一个代码库,它与 AI Shell 接口,可与特定大型语言模型或其他帮助提供程序通信。 用户使用自然语言与代理聊天以获取所需的输出或帮助。 代理作为 C# 类实现,用于从 ILLMAgent
包实现 AIShell.Abstraction
接口。
有关 AIShell.Abstraction
层和 AIShell.Kernel
的详细信息,请参阅 AI Shell 体系结构 文档。
本文是创建 Ollama 语言模型代理的分步指南。 本文的目的是提供一个如何创建代理的简单示例。 存储库的文件夹中有更健壮的 Ollama 代理 AIShell.Ollama.Agent
实现。
先决条件
- .NET 8 SDK 或更高版本
- PowerShell 7.4.6 或更高版本
创建代理的步骤
在此示例中,我们创建一个代理,以使用 Ollama 与语言模型phi3
进行通信。 Ollama 是一个 CLI 工具,用于管理和使用本地构建的 LLM/SLM。
步骤 1:创建新项目
第一步是创建一个新的 classlib 项目。
创建名为
OllamaAgent
的新文件夹运行以下命令以创建新项目:
dotnet new classlib
步骤 2:添加必要的包
在新创建的项目中,需要从 NuGet 库安装 AIShell.Abstraction 包。 使用以下命令安装 NuGet 包:
dotnet add package AIShell.Abstraction --version 1.0.0-preview.2
此命令将包添加到 .csproj
文件。
.csproj
文件应包含以下 XML:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<SuppressNETCoreSdkPreviewMessage>true</SuppressNETCoreSdkPreviewMessage>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AIShell.Abstraction" Version="1.0.0-preview.2" />
</ItemGroup>
</Project>
重要
请务必检查 NuGet 库中的最新版本。
步骤 3:实现代理类
若要实现 ILLMAgent
接口,请修改 Class1.cs
文件。
- 将文件重命名为
OllamaAgent.cs
- 将类重命名为
OllamaAgent
- 在实现中添加代码使用的 .NET 命名空间
using System.Diagnostics;
using System.Text;
using System.Text.Json;
using AIShell.Abstraction;
namespace AIShell.Ollama.Agent;
public sealed class OllamaAgent : ILLMAgent
{
}
步骤 4:添加必要的类成员和方法
接下来,实现代理类的必要变量和方法。 注释提供 OllamaAgent 类的成员的说明。 该 _chatService
成员是 OllamaChatService 类的实例,您将在后续步骤中实现该类。
public sealed class OllamaAgent : ILLMAgent
{
/// <summary>
/// The name of the agent
/// </summary>
public string Name => "ollama";
/// <summary>
/// The description of the agent to be shown at start up
/// </summary>
public string Description => "This is an AI assistant that uses the Ollama CLI tool. Be sure to follow all prerequisites in https://aka.ms/ollama/readme";
/// <summary>
/// This is the company added to `/like` and `/dislike` verbiage for who the telemetry helps.
/// </summary>
public string Company => "Microsoft";
/// <summary>
/// These are samples that are shown at start up for good questions to ask the agent
/// </summary>
public List<string> SampleQueries => [
"How do I list files in a given directory?"
];
/// <summary>
/// These are any optional legal/additional information links you want to provide at start up
/// </summary>
public Dictionary<string, string> LegalLinks { private set; get; }
/// <summary>
/// This is the chat service to call the API from
/// </summary>
private OllamaChatService _chatService;
/// <summary>
/// A string builder to render the text at the end
/// </summary>
private StringBuilder _text;
/// <summary>
/// Dispose method to clean up the unmanaged resource of the chatService
/// </summary>
public void Dispose()
{
_chatService?.Dispose();
}
/// <summary>
/// Initializing function for the class when the shell registers an agent
/// </summary>
/// <param name="config">Agent configuration for any configuration file and other settings</param>
public void Initialize(AgentConfig config)
{
_text = new StringBuilder();
_chatService = new OllamaChatService();
LegalLinks = new(StringComparer.OrdinalIgnoreCase)
{
["Ollama Docs"] = "https://github.com/ollama/ollama",
["Prerequisites"] = "https://aka.ms/ollama/readme"
};
}
/// <summary>
/// Get commands that an agent can register to the shell when being loaded
/// </summary>
public IEnumerable<CommandBase> GetCommands() => null;
/// <summary>
/// Gets the path to the setting file of the agent.
/// </summary>
public string SettingFile { private set; get; } = null;
/// <summary>
/// Refresh the current chat by starting a new chat session.
/// An agent can reset chat states in this method.
/// </summary>
public void RefreshChat() {}
/// <summary>
/// Gets a value indicating whether the agent accepts a specific user action feedback.
/// </summary>
/// <param name="action">The user action.</param>
public bool CanAcceptFeedback(UserAction action) => false;
/// <summary>
/// A user action was taken against the last response from this agent.
/// </summary>
/// <param name="action">Type of the action.</param>
/// <param name="actionPayload"></param>
public void OnUserAction(UserActionPayload actionPayload) {}
/// <summary>
/// Main chat function that takes
/// </summary>
/// <param name="input">The user input from the chat experience</param>
/// <param name="shell">The shell that provides host functionality</param>
/// <returns>Task Boolean that indicates whether the query was served by the agent.</returns>
public async Task<bool> Chat(string input, IShell shell)
{
}
}
对于初始实现,代理返回 “Hello World!”,证明您创建了正确的接口。 您还需要添加一个 try-catch
块,以便在用户尝试取消作时捕获和处理任何异常。
将以下代码添加到 Chat
方法。
public async Task<bool> Chat(string input, IShell shell)
{
// Get the shell host
IHost host = shell.Host;
// get the cancellation token
CancellationToken token = shell.CancellationToken;
try
{
host.RenderFullResponse("Hello World!");
}
catch (OperationCanceledException e)
{
_text.AppendLine(e.ToString());
host.RenderFullResponse(_text.ToString());
return false;
}
return true;
}
步骤 5:添加 Ollama 检查
接下来,需要确保 Ollama 正在运行。
public async Task<bool> Chat(string input, IShell shell)
{
// Get the shell host
IHost host = shell.Host;
// get the cancellation token
CancellationToken token = shell.CancellationToken;
if (Process.GetProcessesByName("ollama").Length is 0)
{
host.RenderFullResponse("Please be sure that Ollama is installed and the server is running. Ensure that you have met all the prerequisites in the README for this agent.");
return false;
}
// Calls to the API will go here
return true;
}
步骤 6:创建数据结构以与聊天服务交换数据
在使用 Ollama API 之前,需要创建向 Ollama API 发送输入并从中接收响应的类。 以下 Ollama 示例 显示代理的输入和响应的格式。
此示例在禁用流式处理的情况下调用 Ollama API。 Ollama 生成单个固定响应。 将来,可以添加流式处理功能,以便在代理接收响应时实时呈现响应。
若要定义数据结构,请在名为 OllamaSchema.cs
的同一文件夹中创建新文件。 将以下代码复制到该文件。
namespace AIShell.Ollama.Agent;
// Query class for the data to send to the endpoint
internal class Query
{
public string prompt { get; set; }
public string model { get; set; }
public bool stream { get; set; }
}
// Response data schema
internal class ResponseData
{
public string model { get; set; }
public string created_at { get; set; }
public string response { get; set; }
public bool done { get; set; }
public string done_reason { get; set; }
public int[] context { get; set; }
public double total_duration { get; set; }
public long load_duration { get; set; }
public int prompt_eval_count { get; set; }
public int prompt_eval_duration { get; set; }
public int eval_count { get; set; }
public long eval_duration { get; set; }
}
internal class OllamaResponse
{
public int Status { get; set; }
public string Error { get; set; }
public string Api_version { get; set; }
public ResponseData Data { get; set; }
}
现在,你已准备好构建使用 Ollama API 的聊天服务所需的部分。 不需要单独的聊天服务类,但抽象化对 API 的调用很有用。
在代理所在的同一文件夹中创建名为 OllamaChatService.cs
的新文件。 将示例代码复制到文件中。
提示
此示例对 Ollama API 使用硬编码的终结点和语言模型。 将来,可以在代理配置文件中定义可配置的参数。
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
using AIShell.Abstraction;
namespace AIShell.Ollama.Agent;
internal class OllamaChatService : IDisposable
{
/// <summary>
/// Ollama endpoint to call to generate a response
/// </summary>
internal const string Endpoint = "http://localhost:11434/api/generate";
/// <summary>
/// Http client
/// </summary>
private readonly HttpClient _client;
/// <summary>
/// Initialization method to initialize the http client
/// </summary>
internal OllamaChatService()
{
_client = new HttpClient();
}
/// <summary>
/// Dispose of the http client
/// </summary>
public void Dispose()
{
_client.Dispose();
}
/// <summary>
/// Preparing chat with data to be sent
/// </summary>
/// <param name="input">The user input from the chat experience</param>
/// <returns>The HTTP request message</returns>
private HttpRequestMessage PrepareForChat(string input)
{
// Main data to send to the endpoint
var requestData = new Query
{
model = "phi3",
prompt = input,
stream = false
};
var json = JsonSerializer.Serialize(requestData);
var data = new StringContent(json, Encoding.UTF8, "application/json");
var request = new HttpRequestMessage(HttpMethod.Post, Endpoint) { Content = data };
return request;
}
/// <summary>
/// Getting the chat response async
/// </summary>
/// <param name="context">Interface for the status context used when displaying a spinner.</param>
/// <param name="input">The user input from the chat experience</param>
/// <param name="cancellationToken">The cancellation token to exit out of request</param>
/// <returns>Response data from the API call</returns>
internal async Task<ResponseData> GetChatResponseAsync(IStatusContext context, string input, CancellationToken cancellationToken)
{
try
{
HttpRequestMessage request = PrepareForChat(input);
HttpResponseMessage response = await _client.SendAsync(request, cancellationToken);
response.EnsureSuccessStatusCode();
context?.Status("Receiving Payload ...");
Console.Write(response.Content);
var content = await response.Content.ReadAsStreamAsync(cancellationToken);
return JsonSerializer.Deserialize<ResponseData>(content);
}
catch (OperationCanceledException)
{
// Operation was cancelled by user.
}
return null;
}
}
步骤 7:调用聊天服务
接下来,需要在主代理类中调用聊天服务。 修改 Chat()
方法以调用聊天服务并向用户呈现响应。 以下示例显示了已完成的 Chat()
方法。
public async Task<bool> Chat(string input, IShell shell)
{
// Get the shell host
IHost host = shell.Host;
// get the cancellation token
CancellationToken token = shell.CancellationToken;
if (Process.GetProcessesByName("ollama").Length is 0)
{
host.RenderFullResponse("Please be sure that Ollama is installed and the server is running. Ensure that you have met all the prerequisites in the README for this agent.");
return false;
}
ResponseData ollamaResponse = await host.RunWithSpinnerAsync(
status: "Thinking ...",
func: async context => await _chatService.GetChatResponseAsync(context, input, token)
).ConfigureAwait(false);
if (ollamaResponse is not null)
{
// render the content
host.RenderFullResponse(ollamaResponse.response);
}
return true;
}
代理代码已完成。
步骤 8:生成并测试代理
接下来,需要生成并测试代码是否按预期工作。 运行以下命令:
dotnet build
此命令在项目的 \bin\Debug\net8.0
文件夹中生成所有必要的包。
若要 aish
加载代理,需要将 .dll
文件复制到 Agents
文件夹中的文件夹。
文件夹名称应与代理名称相同。
您可以在以下两个位置之一安装代理:
- 在安装
Agents
位置下的aish.exe
文件夹中。 用于 AI Shell 的 [安装脚本][08] 安装在%LOCALAPPDATA%\Programs\AIShell
中。 创建%LOCALAPPDATA%\Programs\AIShell\Agents\OllamaAgent
文件夹。 - 或者,在
%USERPROFILE%\.aish\Agents
中安装代理。 创建%USERPROFILE%\.aish\Agents\OllamaAgent
文件夹。
将 .dll
文件复制到创建的代理文件夹。 启动 aish
时,应会看到代理。
AI Shell
v1.0.0-preview.2
Please select an agent to use:
azure
>ollama
openai-gpt
如何共享自己的代理?
无法在集中式存储库中共享您的代理。 建议为此存储库开发自己的代理。 可以在此存储库的“Agent Sharing
”选项卡的 部分中共享分支的链接。 要使用代理,请将代理 dll
文件 agents
放在 的基目录 aish.exe
文件夹中。 AI Shell 会自动从该文件夹加载代理。