当前位置: 首页 > news >正文

C#学习:基于LLM的简历评估程序

前言

在pocketflow的例子中看到了一个基于LLM的简历评估程序的例子,感觉还挺好玩的,为了练习一下C#,我最近使用C#重写了一个。

准备不同的简历:

image-20250528183949844

查看效果:

image-20250528184200364

image-20250528184258947

不足之处是现实的简历应该是pdf格式的,后面可以考虑转化为图片然后用VLM来试试。

C#学习

在使用C#重写的过程中,学习到的一些东西。

KeyValuePair学习

使用到了KeyValuePair

KeyValuePair<TKey, TValue> 是 C# 中一个用于表示键值对的结构体(struct),它是一个泛型类型,可以存储两个相关联的值:一个键(key)和一个值(value)。

主要特点:

不可变性:KeyValuePair 是一个只读结构,一旦创建就不能修改其键或值。

泛型实现

public struct KeyValuePair<TKey, TValue>

其中 TKey 是键的类型,TValue 是值的类型。

使用示例:

// 创建 KeyValuePair
KeyValuePair<string, int> pair = new KeyValuePair<string, int>("Age", 25);// 访问键和值
Console.WriteLine($"Key: {pair.Key}"); // 输出: Key: Age
Console.WriteLine($"Value: {pair.Value}"); // 输出: Value: 25// 在集合中使用
List<KeyValuePair<string, int>> pairs = new List<KeyValuePair<string, int>>
{new KeyValuePair<string, int>("John", 25),new KeyValuePair<string, int>("Mary", 30)
};

KeyValuePair 通常在以下场景中使用:

作为 Dictionary 的枚举结果

Dictionary<string, int> dict = new Dictionary<string, int>();
dict.Add("One", 1);
dict.Add("Two", 2);foreach (KeyValuePair<string, int> kvp in dict)
{Console.WriteLine($"Key: {kvp.Key}, Value: {kvp.Value}");
}

方法返回键值对

public KeyValuePair<string, int> GetPersonInfo()
{return new KeyValuePair<string, int>("Age", 25);
}

LINQ 操作中

var dict = new Dictionary<string, int>();
// ... 添加一些数据
var filtered = dict.Where(kvp => kvp.Value > 10).Select(kvp => kvp.Key);

需要注意的是,如果需要可修改的键值对集合,应该使用 Dictionary<TKey, TValue> 而不是 KeyValuePair 的集合。Dictionary 提供了更多的功能和更好的性能。KeyValuePair 主要用于表示单个键值对关系,特别是在遍历或传递数据时。

YAML内容解析

根据这个提示词:

                string prompt = $@"
评估以下简历并确定候选人是否符合高级技术职位的要求。
资格标准:
- 至少具有相关领域的学士学位
- 至少3年相关工作经验
- 与职位相关的强大技术技能简历内容:
{content}请以YAML格式返回您的评估:
```yaml
candidate_name: [候选人姓名]
qualifies: [true/false]
reasons:- [资格认定/不认定的第一个原因]- [第二个原因(如果适用)]
```
";

LLM会返回一个YAML格式的内容,如下所示:

image-20250528190501147

需要解析这个YAML格式内容。

 public static Dictionary<string, object> ParseSimpleYaml(string yaml){var result = new Dictionary<string, object>();string[] lines = yaml.Split('\n');string currentKey = null;List<string> currentList = null;int currentIndentation = 0;for (int i = 0; i < lines.Length; i++){string line = lines[i].Trim();if (string.IsNullOrEmpty(line) || line.StartsWith("#"))continue;int indentation = lines[i].TakeWhile(c => c == ' ').Count();// Handle list itemsif (line.StartsWith("- ")){if (currentList == null){currentList = new List<string>();result[currentKey] = currentList;}string listItem = line.Substring(2).Trim();currentList.Add(listItem);continue;}// Parse key-value pairsint colonIndex = line.IndexOf(':');if (colonIndex > 0){currentKey = line.Substring(0, colonIndex).Trim();string value = line.Substring(colonIndex + 1).Trim();currentIndentation = indentation;currentList = null;// Check if this is a multi-line value with |if (value == "|"){StringBuilder multiline = new StringBuilder();i++;// Collect all indented lineswhile (i < lines.Length && (lines[i].StartsWith("    ") || string.IsNullOrWhiteSpace(lines[i]))){if (!string.IsNullOrWhiteSpace(lines[i]))multiline.AppendLine(lines[i].Substring(4)); // Remove indentationi++;}i--; // Step back to process the next non-indented line in the outer loopresult[currentKey] = multiline.ToString().Trim();}else if (!string.IsNullOrEmpty(value)){// Simple key-valueresult[currentKey] = value;}}}return result;}

解析结果:

image-20250528190559917

C#条件运算符和空合并运算符

 string name = evaluation.TryGetValue("candidate_name", out var candidateNameValue)? candidateNameValue?.ToString() ?? "未知": "未知";

evaluation.TryGetValue(“candidate_name”, out var candidateNameValue)

  • TryGetValue 是 Dictionary 类的一个方法

  • 它尝试获取键为 “candidate_name” 的值

  • 如果找到了值,返回 true,并将值存储在 candidateNameValue 变量中

  • 如果没找到值,返回 false,candidateNameValue 将为默认值

? 条件运算符(三元运算符)

  • 格式为:condition ? value IfTrue : value If False

  • 如果 TryGetValue 返回 true,执行 :前面的部分

  • 如果 TryGetValue 返回 false,执行 : 后面的 “未知”

candidateNameValue?.ToString()

?. 是空条件运算符

  • 如果 candidateNameValue 不为 null,则调用 ToString()

  • 如果 candidateNameValue 为 null,则返回 null

?? “未知”

?? 是空合并运算符

  • 如果左边的值为 null,则使用右边的值(“未知”)

动态类型转换

List<Dictionary<string, object>> evaluations = null;
var objectList = shared["evaluations"] as List<object>;if (objectList != null)
{evaluations = objectList.Select(item => item as Dictionary<string, object>).Where(dict => dict != null).ToList();
}

这种写法是因为在C#中处理动态类型转换时需要特别小心,尤其是在处理集合类型时。

  1. 首先,shared 是一个 Dictionary<string, object>,这意味着存储在其中的值都是 object 类型。

  2. shared[“evaluations”] 实际上存储的是一个列表,但由于存在字典中时是 object 类型,我们需要安全地将其转换回实际的类型。

  3. 代码使用了两步转换的原因是:

  • 第一步:var objectList = shared[“evaluations”] as List;

这一步将 object 转换为 List,因为列表中的每个元素此时仍然是 object 类型

  evaluations = objectList.Select(item => item as Dictionary<string, object>).Where(dict => dict != null).ToList();

这一步将列表中的每个 object 转换为 Dictionary<string, object>,因为每个评估结果实际上是一个字典

使用 as 操作符而不是直接类型转换(比如 (List)shared[“evaluations”])的原因是:

  • as 操作符在转换失败时会返回 null,而不是抛出异常

  • 这样可以安全地进行类型检查和转换,避免运行时错误

使用 Where(dict => dict != null) 可以过滤掉任何转换失败的项,确保最终的列表中只包含有效的字典对象

这种写法虽然看起来有点复杂,但它是一种安全和健壮的方式来处理动态类型转换,特别是在处理可能包含不同类型数据的集合时。这种方式可以:

  • 避免运行时异常

  • 确保类型安全

  • 优雅地处理可能的空值或无效数据

最后

全部代码已上传至GitHub,地址:https://github.com/Ming-jiayou/PocketFlowSharp/tree/main/PocketFlowSharpSamples.Console/Resume_Qualification_Demo

推荐阅读

“Pocket Flow,一个仅用 100 行代码实现的 LLM 框架”

使用PocketFlow构建Web Search Agent

手把手教你使用C#创建一个WebSearchAgent

使用PocketFlowSharp创建一个Human_Evaluation示例

http://www.xdnf.cn/news/715717.html

相关文章:

  • 4. 算法与分析 (1)
  • 【Dify系列教程重置精品版】第十一章:Dify与slenium
  • Flutter下的一点实践
  • 手动移植FreeRTOS
  • 用 Python 模拟雪花飘落效果
  • Oracle 临时表空间详解
  • Oracle的NVL函数
  • 前端面试题-HTML篇
  • C++:栈帧、命名空间、引用
  • 第三章:地下三层的技术遗产
  • JaCoCo 是什么
  • 系统架构设计师案例分析----经典架构风格特点
  • 挡片/测试晶圆(Dummy Wafer)通俗解析
  • 非线性声学计算与强化学习融合框架:突破复杂环境人机交互的新技术
  • C++进阶--C++11(04)
  • Golang 配置国内代理
  • Android高级开发第二篇 - JNI 参数传递与 Java → C → Java 双向调用
  • 【第4章 图像与视频】4.5 操作图像的像素
  • FastAPI JWT和hash加密
  • 数据中台系统是什么意思?如何实现数据中台的搭建?
  • MySQL JSON数据存储结构与操作
  • 几款主流V30、V60、V90相机SD卡的评测(索尼、闪迪、三星、雷克沙)
  • ultraiso制作U盘镜像 针对win2012及win2016等需要特殊处理
  • Python训练营打卡 Day39
  • 4 串电池保护芯片创芯微CM1341-DAT使用介绍
  • 板凳-------Mysql cookbook学习 (八--2)
  • [yolov11改进系列]基于yolov11引入倒置残差块块注意力机制iEMA的python源码+训练源码
  • 面向低端设备的移动网页调试策略:WebDebugX 在性能瓶颈分析中的应用
  • 1 µs = 10⁻⁶ s
  • 目标检测预测框置信度(Confidence Score)计算方式